第三版:
已实现功能:
1.底部向上滑动可以控制上面View的旋转。
package com.example.wavedemo;
import java.util.ArrayList;
import java.util.List;
import com.example.wavedemo.RollbackGroup.MyRollbackListener;
import com.example.wavedemo.RollbackGroup.MyScrollChangedListener;
import com.example.wavedemo.RotationView.OnRotateChangedListener;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity6 extends Activity {
private List<RotateBean> lists;
private RotationView rotationView;
private ArrowView arrowView;
private RollbackGroup rollbackGroup;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main7);
rotationView = (RotationView) this.findViewById(R.id.rotateView);
arrowView = (ArrowView) this.findViewById(R.id.arrowView);
lists = new ArrayList<RotateBean>();
lists.add(new RotateBean("+0.75", 40, "#7eb445"));
lists.add(new RotateBean("+0.35", 160, "#819d87"));
lists.add(new RotateBean("+0.25", 20, "#e61a5f"));
lists.add(new RotateBean("+0.25", 140, "#000000"));
rotationView.setData(lists);
rotationView.setRotateListener(new OnRotateChangedListener() {
@Override
public void onRotate(int index) {
arrowView.setColor(lists.get(index).getColor());
}
});
rollbackGroup = (RollbackGroup) this.findViewById(R.id.rollbackGroup);
// 滑动下面时,让上面一起滚动
rollbackGroup.setMyScrollChangedListener(new MyScrollChangedListener() {
@Override
public void onScroll(int delta, int top, int perChildHeight) {
int index = top / perChildHeight;
int remainder = top % perChildHeight;
if (index == 0) {
if (remainder <= perChildHeight / 2) {
rotationView.rotateWheel(lists.get(index).getDegrees()
* 1.0f / 2 / (perChildHeight / 2) * delta);
} else {
rotationView.rotateWheel(lists.get(
lists.size() - index - 1).getDegrees()
* 1.0f / 2 / (perChildHeight / 2) * delta);
}
} else {
index=lists.size()-index;
if (remainder <= perChildHeight / 2) {
rotationView.rotateWheel(lists.get(index).getDegrees()
* 1.0f / 2 / (perChildHeight / 2) * delta);
} else {
rotationView.rotateWheel(lists.get(
index - 1).getDegrees()
* 1.0f / 2 / (perChildHeight / 2) * delta);
}
}
}
});
// 滑动下面,让上面回滚
rollbackGroup.setMyRollbackListener(new MyRollbackListener() {
@Override
public void onRollback(int degrees, int delTop, int perChildHeight) {
int index = -delTop / perChildHeight;
int remainder = delTop % perChildHeight;
if (index == 0) {
index = 0;
if (Math.abs(remainder) < perChildHeight / 2) {
rotationView.rotateWheel(lists.get(index).getDegrees()
* 1.0f / 2 / (perChildHeight / 2) * remainder);
} else {
rotationView.rotateWheel(lists.get(
lists.size() - index - 1).getDegrees()
* 1.0f
/ 2
/ (perChildHeight / 2.0f)
* (perChildHeight - Math.abs(remainder)));
}
} else {
Log.e("abc", "index" + index);
index = lists.size() - index;
if (Math.abs(remainder) < perChildHeight / 2) {
rotationView.rotateWheel(lists.get(index).getDegrees()
* 1.0f / 2 / (perChildHeight / 2.0f)
* remainder);
} else {
rotationView.rotateWheel(lists.get(index - 1)
.getDegrees()
* 1.0f/2
/ (perChildHeight / 2)
* (perChildHeight - Math.abs(remainder)));
}
}
}
});
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item);
}
}
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rl_parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingTop="20dp" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="250dp"
android:orientation="horizontal" >
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
<com.example.wavedemo.RotationView
android:id="@+id/rotateView"
android:layout_width="match_parent"
android:layout_height="250dp" />
<View
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1" />
</LinearLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="|"
android:textColor="#5d3d78"
android:visibility="gone" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<com.example.wavedemo.ArrowView
android:id="@+id/arrowView"
android:layout_width="match_parent"
android:layout_height="60dp" />
<com.example.wavedemo.RollbackGroup
android:id="@+id/rollbackGroup"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="20dp"
android:orientation="vertical" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:textColor="#7eb445"
android:text="abc" />
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:textColor="#819d87"
android:text="def" />
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:textColor="#e61a5f"
android:text="hpq" />
<TextView
android:layout_width="match_parent"
android:layout_height="40dp"
android:gravity="center"
android:textColor="#000000"
android:text="lnm" />
</LinearLayout>
</com.example.wavedemo.RollbackGroup>
</FrameLayout>
</LinearLayout>
package com.example.wavedemo;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.v4.view.ViewCompat;
import android.support.v4.widget.ViewDragHelper;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
public class RollbackGroup extends LinearLayout {
private ViewDragHelper viewDragHelper;
private float sensitivity = 1.0f;
private int width;
private int height;
private int childCount;
private int perChildHeight;
private int range;// 拖动的最大范围
private int deltaTop;
private MyScrollChangedListener listener;// 为了控制RotaationView
private MyRollbackListener rollbackListener;// 为了回滚RotationView
@SuppressLint("NewApi")
public RollbackGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public RollbackGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RollbackGroup(Context context) {
this(context, null);
}
public void init() {
setOrientation(LinearLayout.VERTICAL);
if (viewDragHelper == null) {
viewDragHelper = ViewDragHelper.create(this, sensitivity,
new ViewDragHelper.Callback() {
@Override
public boolean tryCaptureView(View arg0, int arg1) {
return true;
}
@Override
public int clampViewPositionHorizontal(View child,
int left, int dx) {
return super.clampViewPositionHorizontal(child,
left, dx);
}
@Override
public int clampViewPositionVertical(View child,
int top, int dy) {
if (top > 0) {
top = 0;
} else if (top < -range) {
top = -range;
}
if (listener != null) {
listener.onScroll(-dy, -top,
perChildHeight);
}
deltaTop = top;
return top;
}
@Override
public void onViewReleased(View releasedChild,
float xvel, float yvel) {
super.onViewReleased(releasedChild, xvel, yvel);
// 向最近的View回滚
int currentIndex = 0;
int finalTop = 0;
if (-deltaTop % perChildHeight != 0) {
currentIndex = -deltaTop / perChildHeight;
if (-deltaTop % perChildHeight < perChildHeight / 2) {
finalTop = -currentIndex * perChildHeight;
} else {
finalTop = -(currentIndex + 1)
* perChildHeight;
}
//回滚RotationView
if(rollbackListener!=null)
{
rollbackListener.onRollback(deltaTop-finalTop,deltaTop,perChildHeight);
}
if (viewDragHelper.smoothSlideViewTo(
getChildAt(0), 0, finalTop)) {
// 返回true, 说明还没有移动到指定位置。需要重绘界面
ViewCompat
.postInvalidateOnAnimation(RollbackGroup.this);
}
}
}
});
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
childCount = ((ViewGroup) getChildAt(0)).getChildCount();
if (childCount >= 1) {
perChildHeight = (int) (height * 1.0f / childCount);
range = (height - perChildHeight);
}
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (viewDragHelper != null) {
return viewDragHelper.shouldInterceptTouchEvent(ev);
}
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (viewDragHelper != null) {
viewDragHelper.processTouchEvent(event);
return true;
}
return super.onTouchEvent(event);
}
@Override
public void computeScroll() {
super.computeScroll();
// 2. 维持动画的继续
if (viewDragHelper.continueSettling(true)) {
// 返回true, 说明还没有移动到指定位置。需要重绘界面
ViewCompat.postInvalidateOnAnimation(this);
}
}
/**
* 为了与RotationView联动
*
* @author huozhenpeng
*
*/
interface MyScrollChangedListener {
void onScroll(int delta, int top, int perChildHeight);
}
public void setMyScrollChangedListener(MyScrollChangedListener listener) {
this.listener = listener;
}
/**
* 为了使RotationView回滚
*
* @author huozhenpeng
*
*/
interface MyRollbackListener {
void onRollback(int degrees,int index,int perChildHeight);
}
public void setMyRollbackListener(MyRollbackListener rollListener) {
this.rollbackListener = rollListener;
}
}
package com.example.wavedemo;
import java.util.List;
import com.example.wavedemo.RollbackGroup.MyScrollChangedListener;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.FontMetrics;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.widget.ImageView;
/**
*
* @author huozhenpeng
*
*/
public class RotationView extends ImageView {
private int width;
private int height;
private Paint paint;
private RectF rectf;
private int paintWidth = 100;
private int margin;
private double startAngle;// 记录开始角度
private Matrix matrix;
private double totalRotation;
private int padding;// 设置padding或者leftpadding
private boolean isFirst = true;
private Bitmap imageScaled;
private Canvas mCanvas;
private TextPaint textpaint;
private int textsize = 50;
private FontMetrics fontMetrics;
private List<RotateBean> datas;
private RotateBean rotateBean;
public int currentIndex = 0;
public RollbackGroup rollbackGroup;
private OnRotateChangedListener listener;
public RotationView(Context context) {
this(context, null);
}
public RotationView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RotationView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
public void init() {
this.setScaleType(ScaleType.MATRIX);
paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
paint.setStrokeWidth(paintWidth);
paint.setStyle(Style.STROKE);
textpaint = new TextPaint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG);
textpaint.setTextSize(textsize);
textpaint.setTextAlign(Align.CENTER);
if (matrix == null) {
matrix = new Matrix();
} else {
matrix.reset();
}
setImageMatrix(matrix);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
padding = getPaddingLeft();
margin = padding + paintWidth / 2;
rectf = new RectF(margin, margin, width - margin, height - margin);
imageScaled = Bitmap.createBitmap(width, height, Config.ARGB_8888);
mCanvas = new Canvas();
mCanvas.setBitmap(imageScaled);
if (isFirst) {
if (datas != null) {
int sumDegrees = 0;
for (int i = 0; i < datas.size(); i++) {
rotateBean = datas.get(i);
paint.setColor(Color.parseColor(rotateBean.getColor()));
mCanvas.drawArc(rectf, sumDegrees, rotateBean.getDegrees(),
false, paint);
sumDegrees += rotateBean.getDegrees();
}
}
}
isFirst = false;
setImageBitmap(imageScaled);
if (datas != null) {
rotateWheel2(90-datas.get(0).getDegrees()/2);
}
totalRotation=datas.get(0).getDegrees()/2;
fontMetrics = textpaint.getFontMetrics();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (datas != null) {
textpaint.setColor(Color.parseColor(datas.get(currentIndex)
.getColor()));
canvas.drawText(
datas.get(currentIndex).getShowData(),
width / 2,
height / 2 - (fontMetrics.ascent + fontMetrics.descent) / 2,
textpaint);
}
}
/**
* 得到(x,y)点的象限值
*
* @return quadrant 1,2,3 or 4
*/
private static int getQuadrant(double x, double y) {
if (x >= 0) {
return y >= 0 ? 1 : 4;
} else {
return y >= 0 ? 2 : 3;
}
}
/**
* 得到(x,y)点的角度
*/
private double getAngle(double x, double y) {
x = x - (width / 2d);
y = height - y - (height / 2d);// (就是wheelHeight/2-y,由于变为数学中的坐标系,所以相当于取了一下反)
switch (getQuadrant(x, y)) {
case 1:
return Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
case 2:
return 180 - Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
case 3:
return 180 + (-1 * Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
case 4:
return 360 + Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI;
default:
return 0;
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
startAngle = getAngle(event.getX(), event.getY());
break;
case MotionEvent.ACTION_MOVE:
double currentAngle = getAngle(event.getX(), event.getY());
rotateWheel((float) (startAngle - currentAngle));
startAngle = currentAngle;
break;
case MotionEvent.ACTION_UP:
totalRotation = totalRotation % 360;
if (totalRotation < 0) {
totalRotation = 360 + totalRotation;
}
MyPoint point = null;
for (int i = 0; i < datas.size(); i++) {
point = datas.get(i).getPoint();
if (point.getX1() < totalRotation
&& totalRotation < point.getX2()) {
rotateWheel((float) (point.getX3() - totalRotation));
currentIndex = i;
if(listener!=null)
{
listener.onRotate(currentIndex);
}
}
}
break;
}
return true;
}
/**
* 要旋转的角度
*
* @param degrees
*/
public void rotateWheel(float degrees) {
matrix.postRotate(degrees, width / 2, height / 2);
setImageMatrix(matrix);
totalRotation = totalRotation + degrees;
}
/**
* 要旋转的角度
*
* @param degrees
*/
public void rotateWheel2(float degrees) {
matrix.postRotate(degrees, width / 2, height / 2);
setImageMatrix(matrix);
}
/**
* 设置数据,寻找规律
*
* degree x1 x2 x3
*
* 0 90 0 90 45
*
* 1 60 300 360 330
*
* 2 50 250 300 225
*
* 3 160 90 250 170
*
*
* @param datas
*/
public void setData(List<RotateBean> datas) {
this.datas = datas;
// 为point属性赋值
for (int i = 0; i < datas.size(); i++) {
MyPoint point = new MyPoint();
int x1 = 0;
int x2 = 0;
int x3 = 0;
if (i == 0) {
x1 = 0;
x2 = datas.get(i).getDegrees();
x3 = x2 / 2;
point.set(x1, x2, x3);
} else {
for (int j = i + 1; j < datas.size(); j++) {
x1 += datas.get(j).getDegrees();
}
x3 = x1 + datas.get(i).getDegrees() / 2
+ datas.get(0).getDegrees();
x1 += datas.get(0).getDegrees();
x2 = x1 + datas.get(i).getDegrees();
point.set(x1, x2, x3);
}
datas.get(i).setPoint(point);
}
init();
}
interface OnRotateChangedListener
{
void onRotate(int index);
}
/**
* 箭头变颜色使用
* @param listener
*/
public void setRotateListener(OnRotateChangedListener listener)
{
this.listener=listener;
}
public int getTotalDegrees()
{
return (int) totalRotation;
}
}
package com.example.wavedemo;
import java.io.Serializable;
public class MyPoint implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
public MyPoint() {
// TODO Auto-generated constructor stub
}
private int x1;
private int x2;
private int x3;
public int getX1() {
return x1;
}
public void setX1(int x1) {
this.x1 = x1;
}
public int getX2() {
return x2;
}
public void setX2(int x2) {
this.x2 = x2;
}
public int getX3() {
return x3;
}
public void setX3(int x3) {
this.x3 = x3;
}
public void set(int x1, int x2, int x3) {
setX1(x1);
setX2(x2);
setX3(x3);
}
@Override
public String toString() {
return "MyPoint [x1=" + x1 + ", x2=" + x2 + ", x3=" + x3 + "]";
}
}
package com.example.wavedemo;
import java.io.Serializable;
public class RotateBean implements Serializable {
/**
*
*/
private static final long serialVersionUID = 1L;
private String showData;
private int degrees;
private boolean isSelected;
private String color;// 十六进制颜色值
private MyPoint point;//只是记录两个点(并非x和y)
public String getShowData() {
return showData;
}
public void setShowData(String showData) {
this.showData = showData;
}
public int getDegrees() {
return degrees;
}
public void setDegrees(int degrees) {
this.degrees = degrees;
}
public boolean isSelected() {
return isSelected;
}
public void setSelected(boolean isSelected) {
this.isSelected = isSelected;
}
public RotateBean(String showData, int degrees) {
super();
this.showData = showData;
this.degrees = degrees;
}
public String getColor() {
return color;
}
public void setColor(String color) {
this.color = color;
}
public MyPoint getPoint() {
return point;
}
public void setPoint(MyPoint point) {
this.point = point;
}
@Override
public String toString() {
return "RotateBean [showData=" + showData + ", degrees=" + degrees
+ ", isSelected=" + isSelected + ", color=" + color
+ ", point=" + point + "]";
}
public RotateBean(String showData, int degrees, String color) {
super();
this.showData = showData;
this.degrees = degrees;
this.color = color;
}
}