地球仪式分布的控件,球体控件

来源:互联网 发布:简述aes算法的加密过程 编辑:程序博客网 时间:2024/04/29 04:12

效果:

这里写图片描述

本人爱好做android各种好玩的效果和交互,欢迎大家交流。

引用大神的成果
http://www.open-open.com/lib/view/open1455706317480.html

原来的效果是这样的
这里写图片描述

我改进的:

  1. 对子控件进行x,y轴的3D翻转,使看起来更真实点;
  2. 使子控件到背面的,变成隐约可见
  3. 触摸事件是跟随手指,并带惯性的滚动
  4. 修改了做动画的方法,使拖动时更流畅

代码:
从大神的工程的基础上重构了实体类Tag.java的一些属性名字,比如用简单的x,y,z表示子控件的3d空间坐标

public class Tag {    private int popularity;  //this is the importance/popularity of the Tag    public float x, y, z; //the center of the 3D Tag    private float loc2DX, loc2DY;    private float scale;    private float[] argb;    private static final int DEFAULT_POPULARITY = 5;

修改了TagCloudView.java。

去掉onLayout方法,改沿用继承FrameLayout了,修改updateChild方法,view的位置,大小,透明度,X,Y旋转计算直接完成,然后invalidate()重新绘制。
修改onTouchEvent,不再根据触摸点离中心有多远,而是根据手指滑动距离,决定X,Y方向的角度偏转

修改对惯性摩擦阻力的一些参数(run方法),由于修改了角度偏转方法,如果还按原来的参数,则阻力太小,根本停不下来

package com.example.user.third;import android.animation.ValueAnimator;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.CornerPathEffect;import android.graphics.Paint;import android.graphics.Path;import android.graphics.PorterDuff;import android.graphics.PorterDuffXfermode;import android.os.Handler;import android.os.Looper;import android.util.AttributeSet;import android.util.Log;import android.view.MotionEvent;import android.view.View;import android.view.ViewTreeObserver;import android.widget.FrameLayout;/** * Copyright © 2016 moxun * <p> * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the “Software”), * to deal in the Software without restriction, including without limitation the * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or * sell copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * <p> * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * <p> * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE * OR OTHER DEALINGS IN THE SOFTWARE. */public class TagCloudView extends FrameLayout implements Runnable, TagsAdapter.OnDataSetChangeListener {    //    private final float TOUCH_SCALE_FACTOR = .8f;    private final float TOUCH_SCALE_FACTOR = 12f;    private final float TRACKBALL_SCALE_FACTOR = 10;    private float tspeed = 2f;    private TagCloud mTagCloud;    private float mAngleX = 0.5f;    private float mAngleY = 0.5f;    private float centerX, centerY;    private float radius;    private float radiusPercent = 0.9f;    private float[] darkColor = new float[]{1f, 0f, 0f, 1};    private float[] lightColor = new float[]{0.9412f, 0.7686f, 0.2f, 1};    public static final int MODE_DISABLE = 0;    public static final int MODE_DECELERATE = 1;    public static final int MODE_UNIFORM = 2;    public int mode;    private boolean isOnTouch = false;    private Handler handler = new Handler(Looper.getMainLooper());    private TagsAdapter tagsAdapter;    public TagCloudView(Context context) {        super(context);        setFocusableInTouchMode(true);        init(context, null);    }    public TagCloudView(Context context, AttributeSet attrs) {        super(context, attrs);        setFocusableInTouchMode(true);        init(context, attrs);    }    public TagCloudView(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);        setFocusableInTouchMode(true);        init(context, attrs);    }    private void init(Context context, AttributeSet attrs) {        mTagCloud = new TagCloud();        if (attrs != null) {            TypedArray typedArray = context.obtainStyledAttributes(attrs, com.moxun.tagcloudlib.R.styleable.TagCloudView);            String m = typedArray.getString(com.moxun.tagcloudlib.R.styleable.TagCloudView_autoScrollMode);            mode = Integer.valueOf(m);            int light = typedArray.getColor(com.moxun.tagcloudlib.R.styleable.TagCloudView_lightColor, Color.GREEN);            setLightColor(light);            int dark = typedArray.getColor(com.moxun.tagcloudlib.R.styleable.TagCloudView_darkColor, Color.GRAY);            setDarkColor(dark);            float p = typedArray.getFloat(com.moxun.tagcloudlib.R.styleable.TagCloudView_radiusPercent, radiusPercent);            setRadiusPercent(p);            float s = typedArray.getFloat(com.moxun.tagcloudlib.R.styleable.TagCloudView_scrollSpeed, 2f);            setScrollSpeed(s);        }        paint.setColor(Color.GRAY);        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));        paint2.setColor(Color.RED);        paint2.setStyle(Paint.Style.STROKE);        paint2.setStrokeWidth(1);        paint3.setColor(0xff000000);//        paint3.setStyle(Paint.Style.STROKE);        paint3.setTextSize(40);        paint3.setStrokeWidth(1);    }    public void setAutoScrollMode(int mode) {        this.mode = mode;    }    public final void setAdapter(TagsAdapter adapter) {        tagsAdapter = adapter;        tagsAdapter.setOnDataSetChangeListener(this);        onChange();        getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {            @Override            public void onGlobalLayout() {                getViewTreeObserver().removeGlobalOnLayoutListener(this);                for (int i = 0; i < getChildCount(); i++) {                    View child = getChildAt(i);                    child.setPivotX(child.getMeasuredWidth() / 2);                    child.setPivotY(child.getMeasuredHeight() / 2);                }                updateChild();            }        });    }    public void setLightColor(int color) {        float[] argb = new float[4];        argb[0] = Color.alpha(color) / 1.0f / 255;        argb[1] = Color.red(color) / 1.0f / 255;        argb[2] = Color.green(color) / 1.0f / 255;        argb[3] = Color.blue(color) / 1.0f / 255;        lightColor = argb.clone();        onChange();    }    public void setDarkColor(int color) {        float[] argb = new float[4];        argb[0] = Color.alpha(color) / 1.0f / 255;        argb[1] = Color.red(color) / 1.0f / 255;        argb[2] = Color.green(color) / 1.0f / 255;        argb[3] = Color.blue(color) / 1.0f / 255;        darkColor = argb.clone();        onChange();    }    public void setRadiusPercent(float percent) {        if (percent > 1f || percent < 0f) {            throw new IllegalArgumentException("percent value not in range 0 to 1");        } else {            radiusPercent = percent;            onChange();        }    }    private void initFromAdapter() {        this.postDelayed(new Runnable() {            @Override            public void run() {                centerX = (getRight() - getLeft()) / 2;                centerY = (getBottom() - getTop()) / 2;                radius = radiusPercent * Math.min(centerX, centerY);                mTagCloud.setRadius((int) radius);                mTagCloud.setTagColorLight(lightColor);//higher color                mTagCloud.setTagColorDark(darkColor);//lower color                mTagCloud.clear();                removeAllViews();                for (int i = 0; i < tagsAdapter.getCount(); i++) {                    TagCloudView.this.mTagCloud.add(new Tag(tagsAdapter.getPopularity(i)));                    addView(tagsAdapter.getView(getContext(), i, TagCloudView.this));                }                mTagCloud.create(true);                mTagCloud.setAngleX(mAngleX);                mTagCloud.setAngleY(mAngleY);                mTagCloud.update();            }        }, 0);    }    public void setScrollSpeed(float scrollSpeed) {        tspeed = scrollSpeed;    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);    }    @Override    protected void onAttachedToWindow() {        super.onAttachedToWindow();        handler.post(this);    }    @Override    protected void onDetachedFromWindow() {        super.onDetachedFromWindow();        handler.removeCallbacksAndMessages(null);    }    private void updateChild() {//        requestLayout();        for (int i = 0; i < getChildCount(); i++) {            View child = getChildAt(i);            if (child.getVisibility() != GONE) {                Tag tag = mTagCloud.get(i);                tagsAdapter.onThemeColorChanged(child, tag.getColor());                float scale = tag.getScale();                child.setScaleX(scale);                child.setScaleY(scale);                if (scale < 1 && scale > 0.9f) {                    scale = 9 * scale - 8;//透明度[0.9,1]区间的强制转为区间[0.1,1]                } else if (scale <= 0.9f) {                    scale = 0.1f;//让转到背面的看起来模糊,                }                child.setAlpha(scale);                int left, top;                left = (int) (centerX + tag.x - child.getMeasuredWidth() / 2f);                top = (int) (centerY + tag.y - child.getMeasuredHeight() / 2f);                child.setX(left);                child.setY(top);                double rx = Math.sqrt(radius * radius - tag.x * tag.x);                float rotationX = (float) (Math.asin(tag.y / rx) * 180 / Math.PI);                float rotationY = (float) (Math.asin(-tag.x / radius) * 180 / Math.PI);                child.setRotationX(rotationX);                child.setRotationY(rotationY);            }        }        invalidate();    }    public void reset() {        mTagCloud.reset();        updateChild();    }    @Override    public boolean onTrackballEvent(MotionEvent e) {        float x = e.getX();        float y = e.getY();        mAngleX = (y) * tspeed * TRACKBALL_SCALE_FACTOR;        mAngleY = (-x) * tspeed * TRACKBALL_SCALE_FACTOR;        mTagCloud.setAngleX(mAngleX);        mTagCloud.setAngleY(mAngleY);        mTagCloud.update();        updateChild();        return true;    }    float x0, y0;    @Override    public boolean onTouchEvent(MotionEvent e) {        float x = e.getX();        float y = e.getY();        switch (e.getAction()) {            case MotionEvent.ACTION_DOWN:                isOnTouch = true;                x0 = x;                y0 = y;                break;            case MotionEvent.ACTION_MOVE:                //rotate elements depending on how far the selection point is from center of cloud//                float dx = x - centerX;//                float dy = y - centerY;                float dx = x - x0;                float dy = y - y0;                x0 = x;                y0 = y;                mAngleX = (dy / radius) * tspeed * TOUCH_SCALE_FACTOR;                mAngleY = (-dx / radius) * tspeed * TOUCH_SCALE_FACTOR;                processTouch();                break;            case MotionEvent.ACTION_UP:            case MotionEvent.ACTION_CANCEL:                isOnTouch = false;                break;        }        return true;    }    private ValueAnimator animator;    private void processTouch() {        if (mTagCloud != null) {            mTagCloud.setAngleX(mAngleX);            mTagCloud.setAngleY(mAngleY);            mTagCloud.update();        }        updateChild();    }    @Override    public void onChange() {        initFromAdapter();    }    @Override    public void run() {        if (!isOnTouch && mode != MODE_DISABLE) {            if (mode == MODE_DECELERATE) {//                if (mAngleX > 0.04f) {//                    mAngleX -= 0.02f;//                }//                if (mAngleY > 0.04f) {//                    mAngleY -= 0.02f;//                }//                if (mAngleX < -0.04f) {//                    mAngleX += 0.02f;//                }//                if (mAngleY < 0.04f) {//                    mAngleY += 0.02f;//                }                if (mAngleX > 0.4f) {                    mAngleX -= 0.1f;                } else if (mAngleX < -0.4f) {                    mAngleX += 0.1f;                } else {                    mAngleX = 0;                }                if (mAngleY > 0.4f) {                    mAngleY -= 0.1f;                } else if (mAngleY < -0.4f) {                    mAngleY += 0.1f;                } else {                    mAngleY = 0;                }            }            processTouch();        }        handler.postDelayed(this, 25);    }    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);    Paint paint2 = new Paint(Paint.ANTI_ALIAS_FLAG);    Paint paint3 = new Paint(Paint.ANTI_ALIAS_FLAG);    @Override    protected void dispatchDraw(Canvas canvas) {        super.dispatchDraw(canvas);        int count = canvas.saveLayer(null, null, Canvas.ALL_SAVE_FLAG);        canvas.drawColor(0x30000000);        canvas.drawCircle(centerX, centerY, radius, paint);        canvas.restoreToCount(count);        Path path = new Path();        Tag tag = mTagCloud.get(0);        path.moveTo(centerX + tag.getX(), centerY + tag.getY());        for (int i = 1; i < getChildCount(); i++) {            tag = mTagCloud.get(i);            float x = centerX + tag.getX();            float y = centerY + tag.getY();            path.lineTo(x, y);        }//        canvas.drawPath(path, paint2);    }}

调用示例

package com.example.user.third;import android.app.Activity;import android.content.Context;import android.content.Intent;import android.os.Bundle;import android.view.Gravity;import android.view.View;import android.view.ViewGroup;import android.widget.ImageView;import android.widget.TextView;import com.example.user.mytest.R;/** * Created by user on 2016/12/7. */public class ThirdActivity extends Activity {    TagCloudView cloudView;    private int[] ss = {R.mipmap.baby1, R.mipmap.jt, R.mipmap.liy, R.mipmap.lyf1, R.mipmap.ym, R.mipmap.mm1, R.mipmap.mm2, R.mipmap.baby1, R.mipmap.jt, R.mipmap.liy, R.mipmap.lyf1, R.mipmap.ym, R.mipmap.mm1, R.mipmap.mm2};    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_third);        cloudView = (TagCloudView) findViewById(R.id.tagCloudView1);        cloudView.setAdapter(new TagsAdapter() {            @Override            public int getCount() {                return ss.length;            }            @Override            public View getView(Context context, int position, ViewGroup parent) {                ImageView imageView = new ImageView(context);                imageView.setLayoutParams(new ViewGroup.LayoutParams(160, 160));                imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);                imageView.setImageResource(ss[position]);                return imageView;            }            @Override            public Object getItem(int position) {                return ss[position];            }            @Override            public int getPopularity(int position) {                return ss.length - position;            }            @Override            public void onThemeColorChanged(View view, int themeColor) {            //Log.d("px", "onThemeColorChanged view= " + view + ",themeColor=" +        Integer.toHexString(themeColor));            //TextView textView = (TextView) view;            // textView.setTextColor(themeColor);            }        });    }}    

自定义控件往往与项目要的效果有些出入,还是要理解了,然后再修改些内容再使用最好

完整代码稍后上传

0 0