自定义展开菜单
2015-12-14 23:24
225 查看
在android开发中应该都自定过进度条,举个个简单的例子,通过重写view 的onDraw方法可以得到一个简单的进度条:
重写onDraw方法
![](http://img.blog.csdn.net/20151215233309732?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
![](http://img.blog.csdn.net/20151215233350691?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
上面是一个简单的进度条,通过不断重绘制作出的效果,再看看下面的
![](http://img.blog.csdn.net/20151215233933969?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
这个控件是通过类似进度条的加载效果来展示出菜单,下面是通过进度条的方式山寨的一个类似控件:
![](http://img.blog.csdn.net/20151216005240545?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center)
这个控件其实不难,要注意的就是icon的绘制和一些位置的计算。下面是代码:
加上一些属性的定义:
下面是Activity的代码
重写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(); } } } }
相关文章推荐
- java相关-- 工作笔记第一天
- 【Android】Activity启动错误的几个解决办法
- 【转】Android 混淆代码总结
- Android中的数据存储之文件存储、SharedPreferences和Pull解析
- 广东省第一届“强网杯”决赛 心得
- postgresql查询分析源码分析-流程
- try-with-resources自动关闭资源
- c++ 浅复制与深复制
- springMVC与struts2的区别
- 互联网科普贴-阿里巴巴国际站是什么
- ORA-01932: ADMIN 选项未授权给角色 'CONNECT'
- 深度学习笔记:windows10+visual studio 2013+cuda7.5+theano+lasagne环境配置
- strcpy,strcmp,strcat,strlen的实现以及大概介绍(上)
- Android4.1 Rotation 小结
- SVN服务器搭建和使用(二)
- ORA-01031: 权限不足
- Android4.1 关于Rotation相关的Configuration整体分析
- C++实现简单的双向链表
- js学习笔记(一)
- NotSerializableException解决方法