地球仪式分布的控件,球体控件
来源:互联网 发布:简述aes算法的加密过程 编辑:程序博客网 时间:2024/04/29 04:12
效果:
本人爱好做android各种好玩的效果和交互,欢迎大家交流。
引用大神的成果
http://www.open-open.com/lib/view/open1455706317480.html
原来的效果是这样的
我改进的:
- 对子控件进行x,y轴的3D翻转,使看起来更真实点;
- 使子控件到背面的,变成隐约可见
- 触摸事件是跟随手指,并带惯性的滚动
- 修改了做动画的方法,使拖动时更流畅
代码:
从大神的工程的基础上重构了实体类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
- 地球仪式分布的控件,球体控件
- AspNetPager分布控件的一些设置
- 按比例分布控件
- 最全JAVA地球上两点间的距离算法!包含球体、椭球体、百度算法
- Repeater和DataList控件也玩分布
- 最常用的一种存储过程分页方式,基于AspNetPager分布控件
- 原谅自己的仪式
- Flash 与分布学构想:球体曲面分布
- 5.osg中用顶点绘制球体并贴上地球纹理
- 控件
- 控件
- 控件
- 控件
- 控件
- 控件
- 控件
- 控件
- 控件
- main 1
- search 页面通常点完search button 后还需要保留页面点击search之前已经输入的那些检索条件的值,通过request getparameter获得。
- php 把数组作为一个元素添加到自己会导致暂时的内存泄露
- 使用自定义对象数组时报nullpointerException
- file_choice
- 地球仪式分布的控件,球体控件
- POJ 2674 Linear world 已翻译
- Ember 翻译——教程八:创建一个 Handlebars helper
- 引导页面小灰点
- file_db
- Android 中 SQLite 性能优化
- file_email
- poj1062 昂贵的聘礼 Dijkstra
- linux 负载均衡 nginx反向代理 tomcat web服务器