您的位置:首页 > 其它

自定义展开菜单

2015-12-14 23:24 225 查看
在android开发中应该都自定过进度条,举个个简单的例子,通过重写view 的onDraw方法可以得到一个简单的进度条:

重写onDraw方法

mPaint.setColor(Color.parseColor("#888888"));
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawArc(rectF,0,mDegree,true,mPaint);
通过线程的mDegree变化可以得到一个简单的进度条





上面是一个简单的进度条,通过不断重绘制作出的效果,再看看下面的



这个控件是通过类似进度条的加载效果来展示出菜单,下面是通过进度条的方式山寨的一个类似控件:



这个控件其实不难,要注意的就是icon的绘制和一些位置的计算。下面是代码:

package com.example.arze.momentview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.shapes.OvalShape;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

/**
* Created by arze on 2015/12/13.
*/
public class DrawProgressView extends View {

private Paint mPaint = new Paint();

//保存第一圈的icon
private List<Drawable> mFirstDrawableList = new ArrayList<>();
//保存第二圈的icon
private List<Drawable> mSecondDrawableList = new ArrayList<>();
//最内部的icon
private Drawable mMainIcon;
// 监听
private OnDrawClickListener mOnDrawClickListener;
// 一些默认常量
private static final int DEFAULT_INNER = 60;
private static final int DEFAULT_FIRST_WIDTH = 80;
private static final int DEFAULT_SECOND_WIDTH = 80;
private static final int DEFAULT_FIRST_COLOR = Color.parseColor("#cacaca");
private static final int DEFAULT_SECOND_COLOR = Color.parseColor("#888888");
private static final int DEFAULT_INNER_COLOR = Color.parseColor("#000000");
private static final int DEFAULT_DEGREE = 1;

//控件的长宽
private int mWidth;
private int mHeight;
//内部圆的半径
private int mInnerCir;
//控件的中心点
private Point mCenterPoint;
//第一圈的宽度
private int mFirstWidth;
//第一圈的位置
private RectF mFirstRectF;
//第二圈的宽度
private int mSecondWidth;
//第二圈的位置
private RectF mSecondRectF;
//第一圈的颜色
private int mFirstColor;
// 第二圈的颜色
private int mSecondColor;
//内部圆的颜色
private int mInnerColor;
//内部圆的位置
private RectF mInnerRectF;

//展开第一圈
private boolean isFirst = false;
//展开第二圈
private boolean isSecond = false;
//展开完第一圈并显示
private boolean isFirstShow = false;
//展开完第二圈并显示
private boolean isSencondShow = false;
//回收第一圈
private boolean isEndFirst = false;
//回收第二圈
private boolean isEndSecond = false;
// 展开第一圈是否完成
private boolean isStartFirstStop = true;
//展开第二圈是否完成
private boolean isStartSecondStop = true;
//回收第一圈是否完成
private boolean isEndFirstStop = true;
//回收第二圈是否完成
private boolean isEndSecondStop = true;

//一些角度属性
private float mDegree = 1;
private float mFirstDrawDegree = 0;
private float mSecondDrawDegree = 0;

/**
* 接口用来设置点击事件
*/
public interface OnDrawClickListener {
void onClick(View view, int position, boolean isFirst, boolean isSecond, boolean isCenter);
}

public void setOnDrawClickListener(OnDrawClickListener listener) {
mOnDrawClickListener = listener;
}

public DrawProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs, 0);

}

public DrawProgressView(Context context) {
super(context);
init(context, null, 0);
}

public DrawProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs, defStyleAttr);
}

/**
* 一些属性的初始化,xml属性的获取
*
* @param context
* @param attrs
* @param defStyleAttr
*/

private void init(Context context, AttributeSet attrs, int defStyleAttr) {
mMainIcon = getResources().getDrawable(R.drawable.main_icon);
mFirstDrawableList.add(getResources().getDrawable(R.drawable.first_icon_0));
mFirstDrawableList.add(getResources().getDrawable(R.drawable.first_icon_1));
mFirstDrawableList.add(getResources().getDrawable(R.drawable.first_icon_2));
mSecondDrawableList.add(getResources().getDrawable(R.drawable.second_icon_0));
mSecondDrawableList.add(getResources().getDrawable(R.drawable.second_icon_1));
mSecondDrawableList.add(getResources().getDrawable(R.drawable.second_icon_2));
mSecondDrawableList.add(getResources().getDrawable(R.drawable.second_icon_3));

TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.DrawView);
if (null != array) {
mFirstWidth = (int) array.getDimension(R.styleable.DrawView_first_width, DEFAULT_FIRST_WIDTH);
mFirstColor = array.getColor(R.styleable.DrawView_first_color, DEFAULT_FIRST_COLOR);
mSecondWidth = (int) array.getDimension(R.styleable.DrawView_second_width, DEFAULT_SECOND_WIDTH);
mSecondColor = array.getColor(R.styleable.DrawView_second_color, DEFAULT_SECOND_COLOR);
mInnerCir = (int) array.getDimension(R.styleable.DrawView_inner_cir, DEFAULT_INNER);
mInnerColor = array.getColor(R.styleable.DrawView_inner_color, DEFAULT_INNER_COLOR);
mDegree = array.getInteger(R.styleable.DrawView_degree, DEFAULT_DEGREE);
array.recycle();
}

}

/**
* 重写onMeasure
*
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//要绘制的长和宽为
mWidth = mHeight = mInnerCir * 2 + mFirstWidth * 2 + mSecondWidth * 2;
setMeasuredDimension(mWidth, mHeight);
//中心点
mCenterPoint = new Point(mWidth / 2, mHeight / 2);
//位置的获取
mInnerRectF = new RectF(
mCenterPoint.x - mInnerCir,
mCenterPoint.y - mInnerCir,
mCenterPoint.x + mInnerCir,
mCenterPoint.y + mInnerCir);
mFirstRectF = new RectF(mCenterPoint.x - mInnerCir - mFirstWidth / 2,
mCenterPoint.y - mInnerCir - mFirstWidth / 2,
mCenterPoint.x + mInnerCir + mFirstWidth / 2,
mCenterPoint.y + mInnerCir + mFirstWidth / 2);
mSecondRectF = new RectF(mCenterPoint.x - mInnerCir - mFirstWidth - mSecondWidth / 2,
mCenterPoint.y - mInnerCir - mFirstWidth - mSecondWidth / 2,
mCenterPoint.x + mInnerCir + mFirstWidth + mSecondWidth / 2,
mCenterPoint.y + mInnerCir + mFirstWidth + mSecondWidth / 2);

}

/**
* 通过角度,宽度,icon在画布的特定位置绘制出icon
*
* @param canvas
* @param degree
* @param width
* @param drawable
*/

private void drawDrawable(Canvas canvas, float degree, int width, Drawable drawable) {
int x = (int) (Math.cos(degree / 180 * Math.PI) * width);
int y = (int) (Math.sin(degree / 180 * Math.PI) * width);
Point point = new Point(mCenterPoint.x + x, mCenterPoint.y + y);
drawable.setBounds(
point.x - drawable.getIntrinsicWidth() / 2,
point.y - drawable.getIntrinsicHeight() / 2,
point.x + drawable.getIntrinsicWidth() / 2,
point.y + drawable.getIntrinsicHeight() / 2);
drawable.draw(canvas);
}

/**
* 重写onDraw
*
* @param canvas
*/

@Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
mPaint.setAntiAlias(true);
mPaint.setColor(mInnerColor);
mPaint.setStyle(Paint.Style.FILL);
canvas.drawOval(mInnerRectF, mPaint);
drawDrawable(canvas, 0, 0, mMainIcon);
mPaint.setStrokeWidth(mFirstWidth);
mPaint.setStyle(Paint.Style.STROKE);
//判断是否正在展开第一圈
if (isFirst) {
mPaint.setColor(mFirstColor);
canvas.drawArc(mFirstRectF, 0, mFirstDrawDegree, false, mPaint);
//要绘制的icon中心点到控件中心点的距离
int width = mInnerCir + mFirstWidth / 2;
//如果角度mFirstDrawDegree > 50开始绘制第一个icon下面相同
if (mFirstDrawDegree > 50) {
float degree = mFirstDrawDegree - 20;
drawDrawable(canvas, degree, width, mFirstDrawableList.get(0));
}

if (mFirstDrawDegree > 170) {
float degree = mFirstDrawDegree - 20 - 120;
drawDrawable(canvas, degree, width, mFirstDrawableList.get(1));
}

if (mFirstDrawDegree > 290) {
float degree = mFirstDrawDegree - 20 - 120 * 2;
drawDrawable(canvas, degree, width, mFirstDrawableList.get(2));
}
}
if (isSecond) {
mPaint.setColor(mSecondColor);
canvas.drawArc(mSecondRectF, 0, mSecondDrawDegree, false, mPaint);
int width = mInnerCir + mFirstWidth + mSecondWidth / 2;
if (mSecondDrawDegree > 20) {
float degree = mSecondDrawDegree - 20;
drawDrawable(canvas, degree, width, mSecondDrawableList.get(0));
}
if (mSecondDrawDegree > 110) {
float degree = mSecondDrawDegree - 20 - 90;
drawDrawable(canvas, degree, width, mSecondDrawableList.get(1));
}
if (mSecondDrawDegree > 200) {
float degree = mSecondDrawDegree - 20 - 90 * 2;
drawDrawable(canvas, degree, width, mSecondDrawableList.get(2));
}
if (mSecondDrawDegree > 290) {
float degree = mSecondDrawDegree - 20 - 90 * 3;
drawDrawable(canvas, degree, width, mSecondDrawableList.get(3));
}
}
//在回收的时候,要判断后控件在什么时候消失就行了
if (isEndFirst) {
float firstEndDegree = 360 - mFirstDrawDegree;
mPaint.setColor(mFirstColor);
canvas.drawArc(mFirstRectF, 0, firstEndDegree, false, mPaint);
int width = mInnerCir + mFirstWidth / 2;
float mMenu1Degree = 30 - mFirstDrawDegree;
float mMenu2Degree = 150 - mFirstDrawDegree;
float mMenu0Degree = 270 - mFirstDrawDegree;
if (mFirstDrawDegree < 30) {
drawDrawable(canvas, mMenu1Degree, width, mFirstDrawableList.get(0));
drawDrawable(canvas, mMenu2Degree, width, mFirstDrawableList.get(2));
drawDrawable(canvas, mMenu0Degree, width, mFirstDrawableList.get(1));
} else if (mFirstDrawDegree < 150) {
drawDrawable(canvas, mMenu2Degree, width, mFirstDrawableList.get(2));
drawDrawable(canvas, mMenu0Degree, width, mFirstDrawableList.get(1));
} else if (mFirstDrawDegree < 270) {
drawDrawable(canvas, mMenu0Degree, width, mFirstDrawableList.get(1));
}
}
if (isEndSecond) {
float secondEndDegree = 360 - mSecondDrawDegree;
mPaint.setColor(mSecondColor);
int width = mInnerCir + mFirstWidth + mSecondWidth / 2;
canvas.drawArc(mSecondRectF, 0, secondEndDegree, false, mPaint);
float mMenu3Degree = 0 - mSecondDrawDegree;
float mMenu4Degree = 270 - mSecondDrawDegree;
float mMenu5Degree = 180 - mSecondDrawDegree;
float mMenu6Degree = 90 - mSecondDrawDegree;
if (mSecondDrawDegree < 90) {
drawDrawable(canvas, mMenu4Degree, width, mSecondDrawableList.get(1));
drawDrawable(canvas, mMenu5Degree, width, mSecondDrawableList.get(2));
drawDrawable(canvas, mMenu6Degree, width, mSecondDrawableList.get(3));
} else if (mSecondDrawDegree < 180) {
drawDrawable(canvas, mMenu4Degree, width, mSecondDrawableList.get(1));
drawDrawable(canvas, mMenu5Degree, width, mSecondDrawableList.get(2));
} else if (mSecondDrawDegree < 270) {
drawDrawable(canvas, mMenu4Degree, width, mSecondDrawableList.get(1));
}

}

}

/**
* 展开第一圈
*/

public void startFirst() {
if (isStartFirstStop) {
isStartFirstStop = false;
isFirst = true;
isEndFirst = false;
mFirstDrawDegree = 0;
new MyFirstThread(this).start();
}
}

/**
* 展开第二圈
*/

public void startSecond() {
if (isStartSecondStop) {
isStartSecondStop = false;
isSecond = true;
isEndSecond = false;
mSecondDrawDegree = 0;
if (isFirstShow())
new MySecondThread(this).start();
}
}

/**
* 回收第一圈
*/

public void endFirst() {
if (isEndFirstStop) {
isEndFirstStop = false;
isFirst = false;
isEndFirst = true;
mFirstDrawDegree = 0;
new MyFirstThreadEnd(this).start();
}
}

/**
* 回收第二圈
*/

public void endSecond() {
if (isEndSecondStop) {
isEndSecondStop = false;
isSecond = false;
isEndSecond = true;
mSecondDrawDegree = 0;
new MySecondThreadEnd(this).start();
}

}

/**
* 判断点击事件
*
* @param event
* @return
*/

@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
int x = (int) event.getX();
int y = (int) event.getY();
if (isFirstShow) {
for (int i = 0; i < mFirstDrawableList.size(); i++) {
//因为在绘制icon的过程中已经将icon在控件的位置setBounds了,所以可以根据这个来判断
if (mFirstDrawableList.get(i).getBounds().contains(x, y)) {
if (null != mOnDrawClickListener)
//控件的位置是从绘制的第一个控件逆时针开始算,下同
mOnDrawClickListener.onClick(this, i, true, false, false);
}
}
}
if (isSencondShow) {
for (int i = 0; i < mSecondDrawableList.size(); i++) {
if (mSecondDrawableList.get(i).getBounds().contains(x, y)) {
if (null != mOnDrawClickListener)
mOnDrawClickListener.onClick(this, i, false, true, false);
}
}
}
//判断是否在内圆,点击的点和中心点的距离是否小于或者等于半径
int r = (int) Math.sqrt(Math.pow(x - mCenterPoint.x, 2) + Math.pow(y - mCenterPoint.y, 2));
if (r <= mInnerCir) {
if (null != mOnDrawClickListener)
mOnDrawClickListener.onClick(this, 0, false, false, true);
}
break;
case MotionEvent.ACTION_UP:
break;
}
return super.onTouchEvent(event);
}

/**
* 展开第一圈的角度变化线程
*/
private class MyFirstThread extends Thread {
private WeakReference<DrawProgressView> mTarget;

public MyFirstThread(DrawProgressView view) {
mTarget = new WeakReference<DrawProgressView>(view);
}

@Override
public void run() {
while (!interrupted()) {
if (null == mTarget.get())
break;
if (410 == mTarget.get().mFirstDrawDegree) {
break;
}
if (180 < mTarget.get().mFirstDrawDegree) {
try {
sleep(3);
mTarget.get().mFirstDrawDegree += mTarget.get().mDegree;
mTarget.get().postInvalidate();
} catch (InterruptedException e) {
e.printStackTrace();
}

} else {
try {
sleep(5);
mTarget.get().mFirstDrawDegree += mTarget.get().mDegree;
mTarget.get().postInvalidate();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
isFirstShow = true;
isStartFirstStop = true;
}
}

/**
* 展开第二圈的角度变化线程
*/

private class MySecondThread extends Thread {
private WeakReference<DrawProgressView> mTarget;

public MySecondThread(DrawProgressView view) {
mTarget = new WeakReference<DrawProgressView>(view);
}

@Override
public void run() {
while (!interrupted()) {
if (null == mTarget.get())
break;
if (380 == mTarget.get().mSecondDrawDegree)
break;
if (180 < mTarget.get().mSecondDrawDegree) {
try {
sleep(5);
mTarget.get().mSecondDrawDegree += mTarget.get().mDegree;
mTarget.get().postInvalidate();
} catch (InterruptedException e) {
e.printStackTrace();
}

} else {
try {
sleep(10);
mTarget.get().mSecondDrawDegree += mTarget.get().mDegree;
mTarget.get().postInvalidate();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
isSencondShow = true;
isStartSecondStop = true;
}
}

/**
* 回收第一圈的角度变化线程
*/

private class MyFirstThreadEnd extends Thread {
private WeakReference<DrawProgressView> mTarget;

public MyFirstThreadEnd(DrawProgressView view) {
mTarget = new WeakReference<DrawProgressView>(view);
}

@Override
public void run() {
while (!interrupted()) {
if (null == mTarget.get())
break;
if (360 == mTarget.get().mFirstDrawDegree) {
break;
}
try {
sleep(5);
mTarget.get().mFirstDrawDegree += mTarget.get().mDegree;
mTarget.get().postInvalidate();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isFirstShow = false;
isEndFirstStop = true;
}
}

/**
* 回收第二圈的角度变化线程
*/

private class MySecondThreadEnd extends Thread {
private WeakReference<DrawProgressView> mTarget;

public MySecondThreadEnd(DrawProgressView view) {
mTarget = new WeakReference<DrawProgressView>(view);
}

@Override
public void run() {
while (!interrupted()) {
if (null == mTarget.get())
break;
if (360 == mTarget.get().mSecondDrawDegree) {
break;
}
try {
sleep(8);
mTarget.get().mSecondDrawDegree += mTarget.get().mDegree;
mTarget.get().postInvalidate();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
isSencondShow = false;
isEndSecondStop = true;
}
}

public boolean isSencondShow() {
return isSencondShow;
}

public boolean isFirstShow() {
return isFirstShow;
}
}


加上一些属性的定义:

<resources>
<declare-styleable name="DrawView">
<attr name="inner_cir" format="dimension" />
<attr name="first_width" format="dimension" />
<attr name="second_width" format="dimension" />
<attr name="first_color" format="color" />
<attr name="second_color" format="color" />
<attr name="inner_color" format="color"/>
<attr name="degree" format="integer"/>
</declare-styleable>
</resources>
其实效果挺不错的,理解了是通过不断重绘得出的代码应该没什么难度,可以考虑icon不固定让动态添加icon,然后计算角度就可以了。

下面是Activity的代码

package com.example.arze.momentview;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

public class MainActivity extends AppCompatActivity implements DrawProgressView.OnDrawClickListener {
DrawProgressView drawProgressView;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
drawProgressView = ((DrawProgressView) findViewById(R.id.drawProgressView));
drawProgressView.setOnDrawClickListener(this);
}

@Override
public void onClick(View view, int position, boolean isFirst, boolean isSecond, boolean isCenter) {

DrawProgressView drawProgressView = (DrawProgressView) view;
if (isCenter) {
if (drawProgressView.isSencondShow()) {
drawProgressView.endSecond();
}
if (drawProgressView.isFirstShow()) {
drawProgressView.endFirst();
} else {
drawProgressView.startFirst();
}
}

if (isFirst) {
if (1 == position) {
if (drawProgressView.isSencondShow())
drawProgressView.endSecond();
else
drawProgressView.startSecond();
}
}

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: