package com.example.buildbankmenuview;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;
import android.widget.TextView;
/**
* created by gongli on 2019/11/27
*/
public class buildBankMenuView extends ViewGroup {
//6个view的坐标,刚开始不变,当监听事件发生,他们的坐标变动是一个圆形。即(x-a)^2+(y-b)^2=r;
//这里有很多种获取view坐标的方案,反正我觉得这种简单。
//监听事件
//对move做判断,是向左,向右,滑动的距离大于多少,是以不同的速度绘制view。
//外部设置过来的数据
//内部控件数量
int innerViewCount;
//设置点击事件
innerViewListener listener;
public void setListener(innerViewListener listener) {
this.listener = listener;
}
interface innerViewListener {
void onInnerViewClick(View view, int position);
void onInnerCenterViewClick(View view);
}
//设置内部小菜单需要的数据,把其添加到父view中。
public void setInnerViewImageAndText(String[] texts, int[] images) {
if (texts == null && images == null) {
throw new IllegalArgumentException("非法参数异常");
}
innerViewCount = images == null ? texts.length : images.length;
if (texts != null && images != null) {
innerViewCount = texts.length;
}
//添加到父类布局。
LayoutInflater mInflater = LayoutInflater.from(getContext());
for (int i = 0; i < innerViewCount; i++) {
final int j = i;
final View view = mInflater.inflate(R.layout.item_innerview, this, false);
ImageView image = view.findViewById(R.id.innerview_image);
TextView text = view.findViewById(R.id.innerview_text);
if (image != null) {
image.setImageResource(images[i]);
image.setVisibility(View.VISIBLE);
image.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onInnerViewClick(view, j);
}
}
});
}
if (text != null) {
text.setVisibility(View.VISIBLE);
text.setText(texts[i]);
}
addView(view);
}
}
public buildBankMenuView(Context context, AttributeSet attrs) {
super(context, attrs);
setPadding(0, 0, 0, 0);
}
//我就爱把变量申明在这里,咋滴不服。
//外部view直径
int diameter;
float innerViewScale = 1 / 4f;
float innerCenterViewScale = 1 / 3f;
float outerViewPaddingScale = 1 / 12f;
float outerViewPaddingSize;
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int mWidthSize;
int mHeigthSize;
//即warp-content情况下
if (widthMode != MeasureSpec.EXACTLY || heightMode != MeasureSpec.EXACTLY) {
//当设置背景图时,拿到背景图的高宽
mHeigthSize = getSuggestedMinimumHeight();
mHeigthSize = mHeigthSize == 0 ? getDefaultWidthBySystem() : mHeigthSize;
mWidthSize = getSuggestedMinimumHeight();
mWidthSize = mWidthSize == 0 ? getDefaultWidthBySystem() : mWidthSize;
} else {
mWidthSize = mHeigthSize = Math.min(widthSize, heightSize);
}
//设置完这句,我们就可以拿到外部view的宽高。
setMeasuredDimension(mWidthSize, mHeigthSize);
diameter = Math.max(getMeasuredHeight(), getMeasuredWidth());
//这里父view传向子view的测量规格是精确大小,当子view布局设为warpcontent时,完全由父view传递的尺寸决定。
//这里是关键,需要详细看源码机制,我几句话是结论,说起来很简单,不懂你还是不懂。
int mode = MeasureSpec.EXACTLY;
int viewCount = getChildCount();
int innerViewsize = (int) (diameter * innerViewScale);
int measureSpec = -1;
for (int i = 0; i < viewCount; i++) {
View childView = getChildAt(i);
if (childView.getVisibility() == GONE) {
continue;
}
if (childView.getId() == R.id.innerview_center) {
measureSpec = MeasureSpec.makeMeasureSpec((int) (diameter * innerCenterViewScale), mode);
} else {
measureSpec = MeasureSpec.makeMeasureSpec(innerViewsize, mode);
}
childView.measure(measureSpec, measureSpec);
}
outerViewPaddingSize = diameter * outerViewPaddingScale;
}
//不要问我这个方法具体做了啥,绝对不是你10分钟之内能弄明白的。
private int getDefaultWidthBySystem() {
WindowManager windowManager = (WindowManager)
getContext().getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics displayMetrics = new DisplayMetrics();
windowManager.getDefaultDisplay().getMetrics(displayMetrics);
return Math.min(displayMetrics.widthPixels, displayMetrics.heightPixels);
}
float startAngle;
float addAngle;
float innerViewSize;
//可以这样拿吧,大概。
float innerCenterViewSize = innerCenterViewScale * diameter;
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
addAngle = 360 / (count - 1);
for (int i = 0; i < count; i++) {
View childView = getChildAt(i);
if (childView.getId() == R.id.innerview_center) {
continue;
}
if (childView.getVisibility() == GONE) {
continue;
}
int left;
int top;
addAngle %= 360;
innerViewSize = innerViewScale * diameter;
//这里计算坐标
//内部两个圆,他们圆心的距离。
float distance = diameter / 2f - outerViewPaddingSize - innerViewSize / 2;
//这里的坐标我得再想想。
top = (int) (diameter / 2 +
Math.round(Math.sin(Math.toRadians(startAngle)) * distance - 1 / 2f * innerViewSize));
left = (int) (diameter / 2 +
Math.round(Math.cos(Math.toRadians(startAngle)) * distance - 1 / 2f * innerViewSize));
int mWidth = (int) innerViewSize;
childView.layout(left, top, left + mWidth, top + mWidth);
startAngle += addAngle;
}
//给中心view设置点击事件。
View viewCenter = findViewById(R.id.innerview_center);
if (viewCenter != null) {
viewCenter.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null) {
listener.onInnerCenterViewClick(v);
}
}
});
}
int left2 = (int) (diameter / 2 - viewCenter.getMeasuredWidth() / 2);
int top2 = left2 + viewCenter.getMeasuredWidth();
viewCenter.layout(left2, left2,
top2, top2);
}
float lastx;
float lasty;
boolean ifFling = false;
int tempAngle;
long downTime;
int quickFlingValve=300;
int noClickValve=3;
autoFlingRunable flingRunable;
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
float nowx = ev.getX();
float nowy = ev.getY();
Log.e("gongli", "nowx=" + nowx + "###" + "nowyx=" + nowy);
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
lastx = nowx;
lasty = nowy;
tempAngle=0;
downTime= System.currentTimeMillis();
if (ifFling) {
removeCallbacks(flingRunable);
ifFling=false;
return true;
}
break;
case MotionEvent.ACTION_MOVE:
//上一次手指的位置。
float start = getAngle(lastx, lasty);
float end = getAngle(nowx, nowy);
if (getQuaqrant(nowx,nowy)==1||getQuaqrant(nowx,nowy)==4){
startAngle+=end-start;
tempAngle+=end-start;
}else {
startAngle+=start-end;
tempAngle+=start-end;
}
requestLayout();
lastx=nowx;
lasty=nowy;
break;
case MotionEvent.ACTION_UP:
float anglePerSecond=tempAngle*1000/ (System.currentTimeMillis()-downTime);
if (anglePerSecond>quickFlingValve){
post(flingRunable=new autoFlingRunable(anglePerSecond));
return true;
}
//这个是优化吧
// if (tempAngle>noClickValve){
// return true;
// }
break;
}
return super.dispatchTouchEvent(ev);
}
public float getAngle(float a, float b) {
//这里取绝对值的方案可以后面考虑下,不用象限这种方案。
// double x = Math.abs(a - diameter / 2d);
// double y = Math.abs(b - diameter / 2d);
double x= a-diameter/2d;
double y= b-diameter/2d;
float angle = (int) (Math.asin(y / Math.hypot(x, y)) * 180 / Math.PI);
return angle;
}
public int getQuaqrant(float x, float y) {
int result;
if (x - diameter / 2 > 0) {
result = y - diameter / 2 < 0 ? 1 : 4;
} else {
result = y - diameter / 2 < 0 ? 2 : 3;
}
return result;
}
private class autoFlingRunable implements Runnable{
float speed;
autoFlingRunable(float speed1){
speed=speed1;
}
@Override
public void run() {
ifFling=true;
if (speed<20){
ifFling=false;
return;
}
startAngle+=(speed/30);
speed/=1.0666f;
//这行代码放哪里无所谓吧。
requestLayout();
postDelayed(this,100);
}
}
}
重复造轮子练练技术,所有源码都在这里。
这个是原创链接:https://blog.csdn.net/lmj623565791/article/details/43131133#commentsedit