博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android Browser学习十 快捷菜单模块: PieMenu的实现(2)
阅读量:6421 次
发布时间:2019-06-23

本文共 9381 字,大约阅读时间需要 31 分钟。

hot3.png

前面说的这些东西其实都是重写view实现view的自由绘制, 但是有些时候, 可能我们还需要这样一种自定义的view, 他们其实

不能称得上是view, 而只是一些对视图的描述, 这很像我们使用animator的时候, 也会定义一些shape, 然后随着时间控制shape的变化, 然后把这些shape draw到画布上.呈现给用户.其实一定程度是实现了一个自己的"view系统".

同样, 在android浏览器的Piemenu中也有这样的类, 这就是PieView, 我感觉更应该叫 SubPieView.

他在Phone上的展现如下:

也就是选中一个扇形之后, 显示下一级菜单. 当然你也可以自定义成那种围绕内层圆弧再次展开的外层的menu.

我们看一下人是如何实现的.首先是其接口:

public interface PieView {        public interface OnLayoutListener {            public void onLayout(int ax, int ay, boolean left);        }        //同意设置布局监听, 在布局的时候做一些事情, 这里主要是刷新tab        public void setLayoutListener(OnLayoutListener l);        //父亲只管layout, 至于以何种方式展现, 孩子来决定        public void layout(int anchorX, int anchorY, boolean onleft, float angle);        //父亲只管draw, 至于以何种方式展现, 孩子来决定        public void draw(Canvas c);        //父亲只管发出onTouchEvent, 至于响应什么事件, 孩子来决定        public boolean onTouchEvent(MotionEvent evt);    }

设计图如下,在不同的设备(手机和平板)上显示的是不同的,.

首先看一下setAdapter也就是view的组装:

这个调用是在PieControlPhone::populateMenu()中实现调用的:

stack.setAdapter(mTabAdapter);

mTabAdapter还是一个BaseAdapter, 其实这个东西的实现可以认识是一个mini版的listview

//类似于listview的setAdapter    public void setAdapter(Adapter adapter) {        mAdapter = adapter;        if (adapter == null) {            if (mAdapter != null) {                mAdapter.unregisterDataSetObserver(mObserver);            }            mViews = null;            mCurrent = -1;        } else {        	//注册监听, 通知 从adapter中拿到各个view进行拼接            mObserver = new DataSetObserver() {                @Override                public void onChanged() {                    buildViews();                }                @Override                public void onInvalidated() {                    mViews.clear();                }            };            //监听更新的通知, 对应adapter的 notifyDataSetChanged            mAdapter.registerDataSetObserver(mObserver);            setCurrent(0);        }    }

我们也可以看看我们熟悉的
adapter.getView的原理:

//从adapter中拿到view进行组装    protected void buildViews() {        if (mAdapter != null) {            final int n = mAdapter.getCount();            if (mViews == null) {                mViews = new ArrayList
(n); } else { mViews.clear(); } mChildWidth = 0; mChildHeight = 0; for (int i = 0; i < n; i++) { View view = mAdapter.getView(i, null, null);//展示了adapter也可以在其他情况中使用 (非listview), 可以用来把数据转换为view显示 view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); mChildWidth = Math.max(mChildWidth, view.getMeasuredWidth()); mChildHeight = Math.max(mChildHeight, view.getMeasuredHeight()); mViews.add(view); } } }

通过这两步就可以组装成一个view列表了,当然listview还有view的复用等, 这个view没有实现这些功能.

为了方便学习, 把整个BasePieView贴上, 代码也不多:

public abstract class BasePieView implements PieMenu.PieView {    protected Adapter mAdapter;    private DataSetObserver mObserver;    protected ArrayList
mViews; protected OnLayoutListener mListener; protected int mCurrent; protected int mChildWidth; protected int mChildHeight; protected int mWidth; protected int mHeight; protected int mLeft; protected int mTop; public BasePieView() { } public void setLayoutListener(OnLayoutListener l) { mListener = l; } //类似于listview的setAdapter public void setAdapter(Adapter adapter) { mAdapter = adapter; if (adapter == null) { if (mAdapter != null) { mAdapter.unregisterDataSetObserver(mObserver); } mViews = null; mCurrent = -1; } else { //注册监听, 通知 从adapter中拿到各个view进行拼接 mObserver = new DataSetObserver() { @Override public void onChanged() { buildViews(); } @Override public void onInvalidated() { mViews.clear(); } }; //监听更新的通知, 对应adapter的 notifyDataSetChanged mAdapter.registerDataSetObserver(mObserver); setCurrent(0); } } public void setCurrent(int ix) { mCurrent = ix; } public Adapter getAdapter() { return mAdapter; } //从adapter中拿到view进行组装 protected void buildViews() { if (mAdapter != null) { final int n = mAdapter.getCount(); if (mViews == null) { mViews = new ArrayList
(n); } else { mViews.clear(); } mChildWidth = 0; mChildHeight = 0; for (int i = 0; i < n; i++) { View view = mAdapter.getView(i, null, null);//展示了adapter也可以在其他情况中使用 (非listview), 可以用来把数据转换为view显示 view.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED); mChildWidth = Math.max(mChildWidth, view.getMeasuredWidth()); mChildHeight = Math.max(mChildHeight, view.getMeasuredHeight()); mViews.add(view); } } } /** * this will be called before the first draw call * needs to set top, left, width, height * 被父亲控制, 开始布局 */ @Override public void layout(int anchorX, int anchorY, boolean left, float angle) { if (mListener != null) { mListener.onLayout(anchorX, anchorY, left); } } //让孩子们去画着玩吧! @Override public abstract void draw(Canvas canvas); protected void drawView(View view, Canvas canvas) { final int state = canvas.save(); canvas.translate(view.getLeft(), view.getTop()); view.draw(canvas); canvas.restoreToCount(state); } //有孩子来决定 根据y判断当前选中view的策略 protected abstract int findChildAt(int y); @Override public boolean onTouchEvent(MotionEvent evt) { int action = evt.getActionMasked(); int evtx = (int) evt.getX(); int evty = (int) evt.getY(); if ((evtx < mLeft) || (evtx >= mLeft + mWidth) || (evty < mTop) || (evty >= mTop + mHeight)) { return false; } switch (action) { case MotionEvent.ACTION_MOVE: View v = mViews.get(mCurrent); setCurrent(Math.max(0, Math.min(mViews.size() -1,//设置显示那个tab findChildAt(evty))));//更加y来判断 指向了哪个tab View v1 = mViews.get(mCurrent); if (v != v1) { v.setPressed(false); v1.setPressed(true); } break; case MotionEvent.ACTION_UP://在手机抬起的时候 通知相应点击事件 mViews.get(mCurrent).performClick(); mViews.get(mCurrent).setPressed(false); break; default: break; } return true; }}

然后是一种孩子的实现, 也很简单:

/** * shows views in a stack * 这是一个显示层叠tab 视图的view适用于phone */public class PieStackView extends BasePieView {    private static final int SLOP = 5;    private OnCurrentListener mCurrentListener;    private int mMinHeight;    public interface OnCurrentListener {        public void onSetCurrent(int index);    }    public PieStackView(Context ctx) {        mMinHeight = (int) ctx.getResources()                .getDimension(R.dimen.qc_tab_title_height);    }    public void setOnCurrentListener(OnCurrentListener l) {        mCurrentListener = l;    }   //当前选中哪个?    @Override    public void setCurrent(int ix) {        super.setCurrent(ix);        if (mCurrentListener != null) {            mCurrentListener.onSetCurrent(ix);            buildViews();//从adapter中拿到view            layoutChildrenLinear();        }    }    /**     * this will be called before the first draw call     * 在绘制之前会被调用进行layout     */    @Override    public void layout(int anchorX, int anchorY, boolean left, float angle) {        super.layout(anchorX, anchorY, left, angle);        buildViews();        mWidth = mChildWidth;        mHeight = mChildHeight + (mViews.size() - 1) * mMinHeight;        mLeft = anchorX + (left ? SLOP : -(SLOP + mChildWidth));        mTop = anchorY - mHeight / 2;        if (mViews != null) {            layoutChildrenLinear();        }    }    //布局其中的child    private void layoutChildrenLinear() {        final int n = mViews.size();        int top = mTop;        int dy = (n == 1) ? 0 : (mHeight - mChildHeight) / (n - 1);        for (View view : mViews) {            int x = mLeft;            view.layout(x, top, x + mChildWidth, top + mChildHeight);//重叠的tab缩略图进行布局            top += dy;        }    }   //真正的绘制view, 谁想显示这个view就把他的canvas传给他就可以绘制出来了.    @Override    public void draw(Canvas canvas) {        if (mViews != null) {            final int n = mViews.size();            for (int i = 0; i < mCurrent; i++) {                drawView(mViews.get(i), canvas);            }            for (int i = n - 1; i > mCurrent; i--) {                drawView(mViews.get(i), canvas);            }            //先绘制其他的tab 最后绘制当前的tab这样就可以把当前的tab整个显示出来,其他tab被他遮挡一部分了            drawView(mViews.get(mCurrent), canvas);        }    }    //被父亲调用, 用来更加用户的y查看选中的是哪个tab    @Override    protected int findChildAt(int y) {        final int ix = (y - mTop) * mViews.size() / mHeight;        return ix;    }}

这样其实我们学习到了另一个自定义view的方式, 完全不继承view, 谁想显示这个view把canvas给我们的view就可以显示相应的展现

也对listview和adapter的原理有了一些了解~

转载于:https://my.oschina.net/sfshine/blog/213982

你可能感兴趣的文章
Spring 注入bean时的初始化和销毁操作
查看>>
java线程同步原理(lock,synchronized)
查看>>
MyEclipse中使用Hql编辑器找不到Hibernate.cfg.xml文件解决方法
查看>>
yRadio以及其它
查看>>
第四节 对象和类
查看>>
闪迪(SanDisk)U盘防伪查询(官方网站)
查看>>
Android onMeasure方法介绍
查看>>
无锁数据结构
查看>>
MySQL的变量查看和设置
查看>>
android onNewIntent
查看>>
XML特殊符号
查看>>
kaptcha可配置项
查看>>
JavaMail邮箱验证用户注册
查看>>
系统时间——ntpd
查看>>
反射实现AOP动态代理模式(Spring AOP实现原理)
查看>>
Http协议与缓存
查看>>
监测超过特定内存阀值进程并结束
查看>>
Linux Centos 查询信息
查看>>
android adb命令
查看>>
python “双”稀疏矩阵转换为最小联通量“单”矩阵
查看>>