zoukankan      html  css  js  c++  java
  • Android学习笔记之如何使用圆形菜单实现旋转效果...

    PS:最近忙于项目的开发,一直都没有去写博客,是时候整理整理自己在其中学到的东西了...

    学习内容:

    1.使用圆形菜单并实现旋转效果..

        Android的圆形菜单我也是最近才接触到,由于在界面中确实是使用到了,因此就去学习了一下圆形菜单的使用,并且实现菜单的旋转效果,类似于摩天轮那样的效果,个人感觉还是蛮不错的,就是在实现的过程中有点麻烦...通过动态加载的方式,使用ViewGroup来实现了这个过程...个人感觉是一个蛮复杂的过程,最后附上源码...上面先附上效果图,其实这个图给人的感觉更像是摩天轮,转动中心位置的时候,几个附带的小圆会进行转动,通过转动我们可以看到被挡住的部分...算是为了实现一种互动吧...直接上实现过程的代码,然后在进行解释...估计看到下面的代码,很多人就恶心了...但是如果坚持看完了,那么就真正学会了...先不要看下面的代码,把代码先过掉...看下面的解释...

    package com.circle.cil_212_app;
    
    import com.example.cil_212_app.R;
    import android.annotation.SuppressLint;
    import android.annotation.TargetApi;
    import android.content.Context;
    import android.os.Build;
    import android.util.AttributeSet;
    
    import android.view.LayoutInflater;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewConfiguration;
    import android.view.ViewGroup;
    
    import android.widget.ImageView;
    import android.widget.TextView;
    import android.widget.Toast;
    
    @TargetApi(Build.VERSION_CODES.CUPCAKE)
    public class CircleMenuLayout extends ViewGroup{
    
        //→_→ 第一部分...
        
        private int mRadius;   //定义layout的半径...
        
        //定义了两个数值...在选取半径长度的时候需要使用...
        private float mMaxChildDimesionRadio = 1/ 4f;
        private float mCenterItemDimesionRadio = 1/ 3f;
        
        private LayoutInflater inflater;  //自定义加载布局...
        
        private double StartAngle;     //定义初始角度...
        
        private String[] ItemTexts = new String[]{"HTML", "CSS", "JS",
                "JQuery", "DOM", "TEMPLETE"};
        private int[] ItemImgs = new int[]{R.drawable.home_mbank_1_normal,R.drawable.home_mbank_2_normal,R.drawable.home_mbank_3_normal,R.drawable.home_mbank_4_normal,R.drawable.home_mbank_5_normal,R.drawable.home_mbank_6_normal};
        
        private int TouchSlop;
        
        /*
         * 加速度检测
         * */
        
        private float DownAngle;
        private float TmpAngle;
        private long DownTime;
        private boolean isFling;  //判断是否在自由旋转...
        
        //→_→  第二部分..
        
        
        public CircleMenuLayout(Context context, AttributeSet attrs) {
            super(context,attrs);
            // TODO Auto-generated constructor stub
            
            inflater=LayoutInflater.from(context);
          
            for(int i=0;i<ItemImgs.length;i++){
                final int j=i;
            
                View view=inflater.inflate(R.layout.web_circle_1, this, false); 
            
                ImageView iv=(ImageView) view.findViewById(R.id.id_circle_menu_item_image);
                TextView tv=(TextView) view.findViewById(R.id.id_circle_menu_item_text);
                iv.setImageResource(ItemImgs[i]);
                tv.setText(ItemTexts[i]);
               
                view.setOnClickListener(new OnClickListener() {
                    
                    @SuppressLint("NewApi")
                    @Override
                    public void onClick(View v) {
                        // TODO Auto-generated method stub
                        Toast.makeText(getContext(), ItemTexts[j], Toast.LENGTH_SHORT).show();
                    }
                });
                addView(view);
            }
          
            TouchSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
        }
        
        
        //→_→ 第三部分
    
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
        
            setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
         
            mRadius=Math.max(getWidth(), getHeight());
            
            final int count =getChildCount();  
            
            int childsize=(int)(mRadius*mMaxChildDimesionRadio);
            
            int childMode=MeasureSpec.EXACTLY;
            
            for(int i=0;i<count;i++){
            
                final View child=getChildAt(i);
             
                if(child.getVisibility()==GONE){
                    continue;
                }
                int makeMeasureSpec=-1;
             
                if(child.getId()==R.id.id_circle_menu_item_center){
                    
                    makeMeasureSpec = MeasureSpec.makeMeasureSpec(
                            (int) (mRadius * mCenterItemDimesionRadio), childMode);
                }else{
                    makeMeasureSpec=MeasureSpec.makeMeasureSpec(childsize, childMode);
                }
      
                child.measure(makeMeasureSpec, makeMeasureSpec);
            }
        }
        
        //→_→ 第四部分
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            // TODO Auto-generated method stub
            int layoutWidth = r - l;
            int layoutHeight = b - t;
          
            int layoutRadius = Math.max(layoutWidth, layoutHeight);
            
            final int childCount=getChildCount();
            int left,top;
            int radius = (int) (layoutRadius * mMaxChildDimesionRadio);
    
            float angleDelay = 360 / (getChildCount() - 1);
            
            for (int i = 0; i < childCount; i++)
            {
                final View child = getChildAt(i);
    
                
                if (child.getId() == R.id.id_circle_menu_item_center)
                    continue;
    
                if (child.getVisibility() == GONE){
                    continue;
                   }
    
                StartAngle %= 360;
    
                
                float tmp = layoutRadius * 1f / 3 - 1 / 22f * layoutRadius;
                
                
                left = layoutRadius
                        / 2
                        + (int) Math.round(tmp
                                * Math.cos(Math.toRadians(StartAngle)) - 1 / 2f
                                * radius);
                top = layoutRadius
                        / 2
                        + (int) Math.round(tmp
                                * Math.sin(Math.toRadians(StartAngle)) - 1 / 2f
                                * radius);
    
                child.layout(left, top, left + radius, top + radius);
                StartAngle += angleDelay;
            }
    
            View cView = findViewById(R.id.id_circle_menu_item_center);
            cView.setOnClickListener(new OnClickListener()
            {
                @Override
                public void onClick(View v){
                
                    Toast.makeText(getContext(), "aa", Toast.LENGTH_LONG).show();
                }
            });
     
            int cl = layoutRadius / 2 - cView.getMeasuredWidth() / 2;
            int cr = cl + cView.getMeasuredWidth();
            cView.layout(cl, cl, cr, cr);
        }
    
        
        //→_→ 第五部分
        private float mLastX;
        private float mLastY;
    
        private FlingRunnable mFlingRunnable;
        
        @Override
        public boolean dispatchTouchEvent(MotionEvent event){
            
            float x = event.getX();
            float y = event.getY();
            switch (event.getAction()){
            //按下操作...事件机制自带的变量...
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                DownAngle = getAngle(x, y);
                DownTime = System.currentTimeMillis();  
                TmpAngle = 0;
                if (isFling){
                    removeCallbacks(mFlingRunnable);
                    isFling = false;
                    return true ; 
                }
                
                break;
       
            case MotionEvent.ACTION_MOVE:
    
             
                float start = getAngle(mLastX, mLastY);
                float end = getAngle(x, y);
                if (getQuadrant(x, y) == 1 || getQuadrant(x, y) == 4){
                
                    StartAngle += end - start;
                    TmpAngle += end - start;
                }else{
                    StartAngle += start - end;
                    TmpAngle += start - end;
                }
             
                requestLayout();
    
                mLastX = x;
                mLastY = y;
    
                break;
           
            case MotionEvent.ACTION_UP:
    
                float anglePrMillionSecond = TmpAngle * 1000
                        / (System.currentTimeMillis() - DownTime);
    
                if (Math.abs(anglePrMillionSecond) > 230 && !isFling){
                
                    post(mFlingRunnable = new FlingRunnable(anglePrMillionSecond));
                }
    
                if(Math.abs(anglePrMillionSecond) >230 || isFling){
                    return true ; 
                }
                break;
            }
            return super.dispatchTouchEvent(event);
            
        }
       
        private float getAngle(float xTouch, float yTouch){
            double x = xTouch - (mRadius / 2d);
            double y = yTouch - (mRadius / 2d);
            return (float) (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
        }
     
        private int getQuadrant(float x, float y){
            int tmpX = (int) (x - mRadius / 2);
            int tmpY = (int) (y - mRadius / 2);
            if (tmpX >= 0){
                return tmpY >= 0 ? 4 : 1;
            }else{
                return tmpY >= 0 ? 3 : 2;
            }
        }
        
        //→_→ 第六部分
        private class FlingRunnable implements Runnable{
    
            private float velocity;
    
            public FlingRunnable(float velocity){
                this.velocity = velocity;
            }
    
            public void run(){
                if ((int) Math.abs(velocity) < 20){
                    isFling = false;
                    return;
                }
           
                isFling = true;
                StartAngle += (velocity / 30);
                velocity /= 1.0666F;
                postDelayed(this, 30);
                requestLayout();
            }
        }
    }

      在这里开始进行正式的解释,我把代码分成了六个部分...这六个部分最核心的地方就属于第三部分和第四部分和第五部分了,因此我就先解释三四五部分...首先是第三个部分,第三个部分是测量部分,因为这个布局中,这七个彩色圆圈图需要我们动态的添加到我们的布局上,为什么要动态布局,而不是静态,因为后面我们需要实现旋转效果,可能有人不明白为什么旋转就要用动态加载布局,其实旋转就是对布局的样式进行持续刷新,每一次刷新都会导致控件的位置的改变,我们总不能把旋转一圈的布局文件全写完吧?因此动态的添加是为了有更高的灵活性..因此我们需要一个测量的过程,因为动态的加载控件时,系统需要对每一个控件的位置进行计算,找到一个合适的ViewGroup(ViewGroup我们可以把它理解成一个容器,这个容器可以放入组件,也可以放入子容器),然后将控件放入到其中,但是系统计算的数据往往不一定是我们想要的那样...因此我在这里使用了第三部分进行计算...计算每一个控件需要多大的空间才能够放得下...这就是第三部分的完整解释...

        //→_→ 第三部分
        
        
        //在动态加载布局的时候,我们需要人为的进行计算...布局的大小...
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
            /*
             * 调用这个方法的目的是为View设置大小...
             * 直接设置成系统根据父容器算出的一个推荐的最小值...
             * */
            setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
            
            //获取半径...
            mRadius=Math.max(getWidth(), getHeight());
            
            final int count =getChildCount(); //获取子控件的数量,这里是7... 
            
            int childsize=(int)(mRadius*mMaxChildDimesionRadio);
            /* 这里涉及到了一个测量的模式,这个模式有三种属性...
             * 
             * MeasureSpec.UNSPECIFIED,父视图不对子视图施加任何限制,子视图可以得到任意想要的大小;
             *
             * MeasureSpec.EXACTLY,父视图希望子视图的大小是specSize中指定的大小;
             *    
               * MeasureSpec.AT_MOST,子视图的大小最多是specSize中的大小。
             * */
            int childMode=MeasureSpec.EXACTLY;
            
            //对所有的子View进行迭代测量...说白了就是这7个View都得进行测量...
            for(int i=0;i<count;i++){
                
                final View child=getChildAt(i);
                //子控件不可显示,直接跳过..
                if(child.getVisibility()==GONE){
                    continue;
                }
                int makeMeasureSpec=-1;
                //子控件为中心图标的时候,设置其半径大小为1/3父容器半径的大小...
                //如果子控件是其他,也就表示为周围空间的时候,设置为父容器半径的1/4大小...
                //测量的步骤在于下面的代码块...
                if(child.getId()==R.id.id_circle_menu_item_center){
                    //此步骤是对数据和模式的一个封装...返回一个48位的int值,高32位表示模式,低16位表示数值...
                    makeMeasureSpec = MeasureSpec.makeMeasureSpec(
                            (int) (mRadius * mCenterItemDimesionRadio), childMode);
                }else{
                    makeMeasureSpec=MeasureSpec.makeMeasureSpec(childsize, childMode);
                }
                //这个步骤才是真正的计算过程...传递过去的仍然是一个48位的值,系统会根据传递过去的模式来对View进行参数设置...
                child.measure(makeMeasureSpec, makeMeasureSpec);
            }
        }

     第四部分其实就是正式进行布局了,但是布局我们不能随便去布,否则不会出现想要的效果,这里涉及到了一个数学上的知识...中心位置的圆圈是很好测的,但是如何测中心圆旁边的小圆位置才是关键..因此这里用了一个数学知识...

    这就是如何测出中心圆圈到小圆圆心距离的测量方法...其实就是为了求出r和小圆圆心坐标...算出了这个坐标点的位置,这样就可以进行放置了,这个坐标点的位置是一点点找出来的...需要进行尝试,只是一个计算的过程,有心的人只要研究就能弄懂...无心的人给他解释也是白搭...这就是第四部分的目的,就是为了算出位置...

        //→_→ 第四部分
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            // TODO Auto-generated method stub
            int layoutWidth = r - l;
            int layoutHeight = b - t;
            
            //对父容器进行布局...
            int layoutRadius = Math.max(layoutWidth, layoutHeight);
            
            final int childCount=getChildCount();
            int left,top;
            int radius = (int) (layoutRadius * mMaxChildDimesionRadio);
    
            //根据子元素的个数,设置角度...
            float angleDelay = 360 / (getChildCount() - 1);
            
            for (int i = 0; i < childCount; i++)
            {
                final View child = getChildAt(i);
    
                
                if (child.getId() == R.id.id_circle_menu_item_center)
                    continue;
    
                if (child.getVisibility() == GONE){
                    continue;
                   }
    
                //取角度值..
                StartAngle %= 360;
    
                
                float tmp = layoutRadius * 1f / 3 - 1 / 22f * layoutRadius;
                
                
                left = layoutRadius
                        / 2
                        + (int) Math.round(tmp
                                * Math.cos(Math.toRadians(StartAngle)) - 1 / 2f
                                * radius);
                top = layoutRadius
                        / 2
                        + (int) Math.round(tmp
                                * Math.sin(Math.toRadians(StartAngle)) - 1 / 2f
                                * radius);
    
                // Log.e("TAG", "left = " + left + " , top = " + top);
    
                //由于前面还有1/8长度用来放置那个文本框...因此需要求出整体父容器的定位点...
                child.layout(left, top, left + radius, top + radius);
                StartAngle += angleDelay;
            }
    
            View cView = findViewById(R.id.id_circle_menu_item_center);
            cView.setOnClickListener(new OnClickListener()
            {
                @Override
                public void onClick(View v){
                
                    Toast.makeText(getContext(), "aa", Toast.LENGTH_LONG).show();
                }
            });
            
            //设置中心...
            int cl = layoutRadius / 2 - cView.getMeasuredWidth() / 2;
            int cr = cl + cView.getMeasuredWidth();
            cView.layout(cl, cl, cr, cr);
        }

      第五部分和三四没有关联,第五部分是实现旋转过程的一个重要过程...这里我重写了dispatchTouchEvent来实现...这和上一篇是存在关联的...第五部分其实就很简单了,就是实现旋转,旋转时我们需要获取旋转的角度值和坐标值,获取坐标值的目的是为了判断角度值,角度值获取到了后,传递给第六部分开启线程...系统才会按照指定的角度对布局进行持续的刷新...

        //→_→ 第五部分
        private float mLastX;
        private float mLastY;
    
        private FlingRunnable mFlingRunnable; //定义了一个线程...为实现第六部分定义了一个对象...
        
        @Override
        public boolean dispatchTouchEvent(MotionEvent event){
            
            //随手指滑动特效...
            float x = event.getX();
            float y = event.getY();
            switch (event.getAction()){
            //按下操作...事件机制自带的变量...
            case MotionEvent.ACTION_DOWN:
                mLastX = x;
                mLastY = y;
                DownAngle = getAngle(x, y);//获取角度...
                DownTime = System.currentTimeMillis();  //系统的当前时间...
                TmpAngle = 0;
                
                //如果当前在进行快速滚动,那么移除对快速移动的回调...其实就是如果这个界面正在旋转,在旋转的期间我DOWN了一下,那么直接就停止旋转...
                if (isFling){
                    removeCallbacks(mFlingRunnable);
                    isFling = false;
                    return true ; 
                }
                
                break;
            //移动操作...
            case MotionEvent.ACTION_MOVE:
    
                //获取开始和结束后的角度...我们这里移动的是角度...因此需要获取角度...
                float start = getAngle(mLastX, mLastY);
                float end = getAngle(x, y);
    
                
                //判断x,y的值是否在1,4象限...象限相比大家都明白,是为了获取角度值...
                if (getQuadrant(x, y) == 1 || getQuadrant(x, y) == 4){
                 //在一四象限角度为正...
                    StartAngle += end - start;
                    TmpAngle += end - start;
                }else{
                    StartAngle += start - end;
                    TmpAngle += start - end;
                }
                
                
                //重新对布局进行设置...其实就是不断刷新布局的一个过程...
                requestLayout();
    
                //将初始值设置为旋转后的值...
                mLastX = x;
                mLastY = y;
    
                break;
            //抬起操作...
            case MotionEvent.ACTION_UP:
    
                //计算每秒钟移动的角度...
                float anglePrMillionSecond = TmpAngle * 1000
                        / (System.currentTimeMillis() - DownTime);
    
                //如果数值大于这个指定的数值,那么就会认为是加速滚动...
                if (Math.abs(anglePrMillionSecond) > 230 && !isFling){
                    //开启一个新的线程,让其进行自由滚动...
                    post(mFlingRunnable = new FlingRunnable(anglePrMillionSecond));
                }
    
                if(Math.abs(anglePrMillionSecond) >230 || isFling){
                    return true ; 
                }
                break;
            }
            return super.dispatchTouchEvent(event);
            
        }
        //这里用来测试旋转的角度...算出角度之后返回...返回给上面的方法...
        private float getAngle(float xTouch, float yTouch){
            double x = xTouch - (mRadius / 2d);
            double y = yTouch - (mRadius / 2d);
            return (float) (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
        }
        //在这里我们对坐标值进行一个判断..然后把坐标值返回...目的是测试角度...
        private int getQuadrant(float x, float y){
            int tmpX = (int) (x - mRadius / 2);
            int tmpY = (int) (y - mRadius / 2);
            if (tmpX >= 0){
                return tmpY >= 0 ? 4 : 1;
            }else{
                return tmpY >= 0 ? 3 : 2;
            }
        }

      而第六部分其实就没什么了,因为这个界面在旋转的时候,我们不能让他一直转,迟早要停下来,因此我们需要有一个线程来帮助我们持续对界面刷新,并且控制刷新的速度,随着velocity这个值越来越小刷新的速度也就越来越慢,最后就静止不动,这样给人的感觉就是一个减速的过程...

        //→_→ 第六部分
        private class FlingRunnable implements Runnable{
    
            private float velocity;
    
            public FlingRunnable(float velocity){
                this.velocity = velocity;
            }
    
            public void run(){
                if ((int) Math.abs(velocity) < 20){
                    isFling = false;
                    return;
                }
                //减速旋转...
                isFling = true;
                StartAngle += (velocity / 30);
                velocity /= 1.0666F;
                postDelayed(this, 30);//需要保证时刻对页面进行刷新..因为始终要进行新的布局...
                requestLayout();
            }
        }
    }

      第二部分其实就是加载旁边那个TextView和其背景的一个过程,并定义了一些相关资源...第一部分就更不用说了,一些变量的定义...看懂了三四五六部分,自然知道那些变量的作用了...最后贴一下xml文件和Activity的源代码....

    Activity....

    package com.example.cil_212_app;
    
    import android.os.Bundle;
    import android.app.Activity;
    import android.content.Intent;
    import android.view.KeyEvent;
    import android.view.Menu;
    
    public class Web extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.web_circle);
        }
    
        @Override
        public boolean onKeyDown(int keyCode, KeyEvent event){
            if(keyCode==KeyEvent.KEYCODE_BACK){
                Intent intent=new Intent(Web.this,CoverActivity.class);
                startActivity(intent);
                this.finish();
            }
            return super.onKeyDown(keyCode, event);
            
        }
        @Override
        public boolean onCreateOptionsMenu(Menu menu) {
            // Inflate the menu; this adds items to the action bar if it is present.
            getMenuInflater().inflate(R.menu.android, menu);
            return true;
        }
    
    }

    xml...补充一点就是我自己定义了一个资源文件...用来存放id信息使用的....这样可以在布局中直接进行引用....

    <?xml version="1.0" encoding="utf-8"?>
    <resources>
        <item name="id_circle_menu_item_image" type="id"/>
        <item name="id_circle_menu_item_text" type="id"/>
         <item name="id_circle_menu_item_center" type="id"/>
    </resources>
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"
        android:orientation="vertical" >
        <ImageView 
            android:id="@id/id_circle_menu_item_image"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"/>
    
        <TextView 
            android:id="@id/id_circle_menu_item_text"
            android:layout_height="wrap_content"
            android:layout_width="wrap_content"
            android:textColor="@android:color/black"
            android:textSize="14dip"/>
    </LinearLayout>
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@drawable/web_bg"
        android:gravity="center_vertical"
        android:orientation="horizontal" >
        <LinearLayout
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1.0"
            android:background="@drawable/turnplate_bg_left"
            android:gravity="center"
            android:orientation="vertical" >
    
            <TextView
                android:layout_width="fill_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:text="Web 开发"
                android:textColor="#236B8E"
                android:textStyle="bold|italic"
                android:textSize="18dp" />
    
            <TextView
                android:layout_width="fill_parent"
                android:gravity="center"
                android:layout_height="wrap_content"
                android:textAlignment="textStart"
                android:layout_marginTop="5dp"
                android:textStyle="bold|italic"
                android:text="Web页面开发,快速学会前台页面制作."
                android:textColor="#236B8E"
                android:textSize="13.5dip" />
        </LinearLayout>
    
        <FrameLayout
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" >
    
            <com.circle.cil_212_app.CircleMenuLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:background="@drawable/turnplate_bg_right" >
    
                <RelativeLayout
                    android:id="@id/id_circle_menu_item_center"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content" >
    
                    <ImageView
                        android:layout_width="104.0dip"
                        android:layout_height="104.0dip"
                        android:layout_centerInParent="true"
                        android:background="@drawable/turnplate_center_unlogin" />
    
                   
                  <!--  <ImageView
                        android:layout_width="116.0dip"
                        android:layout_height="116.0dip"
                        android:layout_centerInParent="true"
                        android:background="@drawable/turnplate_mask_unlogin_normal" />-->
                </RelativeLayout>
            </com.circle.cil_212_app.CircleMenuLayout>
        </FrameLayout>
    </LinearLayout>

    源码地址:http://files.cnblogs.com/files/RGogoing/com_Draker.zip

  • 相关阅读:
    [原]零基础学习SDL开发之在Android使用SDL2.0渲染PNG图片
    [原]零基础学习SDL开发之在Android使用SDL2.0显示BMP叠加图
    [原]零基础学习SDL开发之在Android使用SDL2.0显示BMP图
    LLBLGen Pro ORM 生成器
    ODATA4 及实现
    所见即所得的网页设计工具 Macaw
    Unity 3D ---引擎行业的新宠
    金蝶有关的网站及服务
    Bitcoin 比特币, LTC(litecoin)莱特币,
    SONY 手提 realtek high definition audio driver
  • 原文地址:https://www.cnblogs.com/RGogoing/p/4676654.html
Copyright © 2011-2022 走看看