精华内容
下载资源
问答
  • 鸿洋android屏幕适配四部曲-传送门 Android 屏幕适配方案 https://blog.csdn.net/lmj623565791/article/details/45460089 Android 百分比布局库(percent-support-lib) 解析与扩展 ... An...

    鸿洋android屏幕适配四部曲-传送门

    两分钟理解Android中PX、DP、SP的区别

    https://blog.csdn.net/donkor_/article/details/77680042

    屏幕分辨率查询

    这样,我尝试用一句话来说明白px和dp的关系,以及为什么要使用两种单位。
    android手机市场因为还没有完全垄断的企业出现,所以屏幕的种类相当多,所以需要一种自适应单位,让同一个单位长度在所有手机上显示的物理尺寸完全一样(注意,是物理尺寸,举个例子,希望实现同一个长度在所有手机上显示的长度都为1厘米,这样的效果),这样的效果单纯使用px是不可能达到的,所以才需要引入dp。看一下公式:

    – PPI = √(长度像素数² + 宽度像素数²) / 屏幕对角线英寸数
    – dp*ppi/160 = px。比如1dp x 320ppi/160 = 2px

    再明白不过了,ppi是两者之间的桥梁,那么ppi是什么呢,是屏幕上对角线上每一英寸所对应的像素数目。通过ppi就能将屏幕像素和实际物理长度进行挂钩了。那么为什么还需要一个160参数呢?我尝试揣摩设计者的初衷,我们在设计界面,设定长度的时候往往希望设定整数值对吧,设定个0.0023这种长度其实很尴尬的不是吗,所以取了一个折衷值,让dp值既不会太大也不会太小。

    Android 屏幕适配方案

    https://blog.csdn.net/lmj623565791/article/details/45460089

    Android 百分比布局库(percent-support-lib) 解析与扩展

    https://blog.csdn.net/lmj623565791/article/details/46695347

    Android 增强版百分比布局库 为了适配而扩展

    https://blog.csdn.net/lmj623565791/article/details/46767825

    Android AutoLayout全新的适配方式 堪称适配终结者

    https://blog.csdn.net/lmj623565791/article/details/49990941

    展开全文
  • 鸿洋Android 面试要点答案整理

    千次阅读 2016-07-18 20:31:36
    Android系统如何在多个资源文件夹下查找匹配最合适的资源 2.3 虚拟机 Java内存模型 Android虚拟机的特点 Dalvik和Art的区别 熟悉垃圾回收的实现机制,了解虚拟机的GC类型 2.4 View相关 常用组件的...

    信息来源:
    http://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650820648&idx=1&sn=cb9ee924f2ded3358dd6c256803cc687&scene=0#wechat_redirect

    以下问题是引用鸿神的文章:

    1、Java技术

    1.1 Java基础

    对抽象、继承、多态的理解
    JAVA的抽象、封装、继承、多态 http://blog.csdn.net/xieqian923/article/details/8183363

    泛型的作用及使用场景
    http://www.cnblogs.com/lwbqqyumidi/p/3837629.html
    枚举的特点及使用场景
    http://www.jb51.net/article/78351.htm
    线程sleep和wait的区别
    JAVA反射机制
    weak/soft/strong引用的区别
    Object的hashCode()与equals()的区别和作用
    http://www.360doc.com/content/14/0527/09/15113968_381327452.shtml

    1.2 集合类

    JAVA常用集合类功能、区别和性能
    并发相关的集合类
    部分常用集合类的内部实现方式

    1.3 多线程相关

    Thread、Runnable、Callable、Futrue类关系与区别
    JDK中默认提供了哪些线程池,有何区别
    线程同步有几种方式,分别阐述在项目中的用法
    在理解默认线程池的前提下,自己实现线程池

    1.4 字符

    String的不可变性
    StringBuilder和StringBuffer的区别
    字符集的理解:Unicode、UTF-8、GB2312等
    正则表达式相关问题

    1.5 注解

    注解的使用
    注解的级别及意义
    如何自定义注解

    2、Android技术

    2.1 Android基础

    四大组件的意义及使用,生命周期回调及意义
    Android四大基本组件介绍与生命周期 http://www.cnblogs.com/bravestarrhu/archive/2012/05/02/2479461.html
    AsyncTask、Handler的使用
    Android系统层次框架结构
    AsyncTask的实现方式
    AsyncTask使用的时候应该注意什么
    Android常见的存储方式
    Looper、Handler和MessageQueue的关系
    Activity的启动流程(考察对Framwork的熟悉程度)
    多进程开发的注意事项(Application类区分进程,进程间内存不可见、进程间通讯方式)

    2.2 Resource相关

    .9图片的意义
    style和theme的作用及用法
    答:
    正确使用 Android 的 Theme 和 Style http://www.tuicool.com/articles/ZjEZFj
    dpi、sp、px的区别以及转换关系
    raw和assets文件夹的作用,二者有何区别
    Android系统如何在多个资源文件夹下查找匹配最合适的资源

    2.3 虚拟机

    Java内存模型
    Android虚拟机的特点
    Dalvik和Art的区别
    熟悉垃圾回收的实现机制,了解虚拟机的GC类型

    2.4 View相关

    常用组件的使用:ListView、RecyclerView及Adapter的使用
    View之间的继承关系
    Invalidate与postInvalidate的区别
    自定义View的实现方式(根据项目经验询问相关组件)。
    onMeasure/onLayout/onDraw的作用
    Paint、Matrix、Shader等绘制相关类的方法作用
    详细描述事件分发机制

    2.5 动画

    Android有哪些动画的实现方式
    Interpolator类的意义和常用的Interpolator
    ViewAnimation与属性动画有什么区别
    如何自定义ViewAnimation
    属性动画的实现原理

    2.6 图片处理

    一般项目中如何加载大图
    图片压缩的方式
    如何不压缩图片加载高清图
    图片加载过程中,一般会使用缓存,这个缓存的主要作用是什么
    谈谈自己熟悉的图片加载框架

    3、项目经验

    3.1 开发工具

    代码管理工具:SVN、Git
    常用的调试工具:Hierarchy Viewer、DDMS等
    CI工具:Lint检查,FindBugs、Jenkins
    内存分析工具:dumpsys、memoinfo、MAT

    3.2 项目工程

    APP更新策略:推送或者拉取?
    如何统一处理错误崩溃
    如何进行用户行为收集
    日志管理有哪些要点?(Debug包开放,release包关闭,错误日志反馈)
    打包方式Ant、Maven、Gradle
    机型适配情况

    3.3 开放平台

    是否使用过第三方平台
    常用开放平台的熟悉度(微信、QQ、微博、支付宝等常用的支付和分享)
    是否进行过对第三方平台的统一封装?
    是否自己开发过SDK?

    3.4 设计相关

    有哪些常用的设计模式、设计原则
    设计模式在Android源码中的应用
    设计模式在项目中的应用
    项目中的分包方式
    MVC在android中的应用,利弊
    android项目中的常见层次结构,包划分
    有没有设计过项目中的层级结构、包划分

    4、wyyl1的补充

    线程和进程的区别
    dp、sp、APPUI设计中的48dp定律原则

    Android TouchEvent事件传递机制
    http://blog.csdn.net/morgan_xww/article/details/9372285/

    TCP/IP、Http、Socket的区别
    http://blog.csdn.net/skyboy11yk/article/details/24304461

    展开全文
  • Adapter模式实战-重构鸿洋Android建行圆形菜单
                         

    对于很多开发人员来说,炫酷的UI效果是最吸引他们注意力的,很多人也因为这些炫酷的效果而去学习一些比较知名的UI库。而做出炫酷效果的前提是你必须对自定义View有所理解,作为90的小民自然也不例外。特别对于刚处在开发初期的小民,对于自定义View这件事觉得又神秘又帅气,于是小民决定深入研究自定义View以及相关的知识点。

    在此之前我们先来看看洋神的原版效果图:

    原文链接在这里,感谢鸿洋的分享!!!

    记得那是2014年的第一场雪,比以往时候来得稍晚一些。小民的同事洋叔是一位资深的研发人员,擅长写UI特效,在开发领域知名度颇高。最近洋叔刚发布了一个效果不错的圆形菜单,这个菜单的每个Item环形排布,并且可以转动。小民决定仿照洋叔的效果实现一遍,但是对于小民这个阶段来说只要实现环形布局就不错了,转动部分作为下个版本功能,就当作自定义View的练习了。

    在google了自定义View相关的知识点之后,小民就写好了这个圆形菜单布局视图,我们一步一步来讲解,代码如下:

    // 圆形菜单public class CircleMenuLayout extends ViewGroup {    // 圆形直径    private int mRadius;    // 该容器内child item的默认尺寸    private static final float RADIO_DEFAULT_CHILD_DIMENSION = 1 / 4f;    // 该容器的内边距,无视padding属性,如需边距请用该变量    private static final float RADIO_PADDING_LAYOUT = 1 / 12f;    // 该容器的内边距,无视padding属性,如需边距请用该变量    private float mPadding;    // 布局时的开始角度    private double mStartAngle = 0;    // 菜单项的文本    private String[] mItemTexts;    // 菜单项的图标    private int[] mItemImgs;    // 菜单的个数    private int mMenuItemCount;    // 菜单布局资源id    private int mMenuItemLayoutId = R.layout.circle_menu_item;    // MenuItem的点击事件接口    private OnItemClickListener mOnMenuItemClickListener;    public CircleMenuLayout(Context context, AttributeSet attrs) {        super(context, attrs);        // 无视padding        setPadding(0, 0, 0, 0);    }    // 设置菜单条目的图标和文本    public void setMenuItemIconsAndTexts(int[] images, String[] texts) {        if (images == null && texts == null) {            throw new IllegalArgumentException("菜单项文本和图片至少设置其一");        }        mItemImgs = images;        mItemTexts = texts;        // 初始化mMenuCount        mMenuItemCount = images == null ? texts.length : images.length;        if (images != null && texts != null) {            mMenuItemCount = Math.min(images.length, texts.length);        }        // 构建菜单项        buildMenuItems();    }    // 构建菜单项    private void buildMenuItems() {        // 根据用户设置的参数,初始化menu item        for (int i = 0; i < mMenuItemCount; i++) {            View itemView = inflateMenuView(i);            // 初始化菜单项            initMenuItem(itemView, i);            // 添加view到容器中            addView(itemView);        }    }    private View inflateMenuView(final int childIndex) {        LayoutInflater mInflater = LayoutInflater.from(getContext());        View itemView = mInflater.inflate(mMenuItemLayoutId, this, false);        itemView.setOnClickListener(new OnClickListener() {            @Override            public void onClick(View v) {                if (mOnMenuItemClickListener != null) {                    mOnMenuItemClickListener.onClick(v, childIndex);                }            }        });        return itemView;    }    private void initMenuItem(View itemView, int childIndex) {        ImageView iv = (ImageView) itemView                .findViewById(R.id.id_circle_menu_item_image);        TextView tv = (TextView) itemView                .findViewById(R.id.id_circle_menu_item_text);        iv.setVisibility(View.VISIBLE);        iv.setImageResource(mItemImgs[childIndex]);        tv.setVisibility(View.VISIBLE);        tv.setText(mItemTexts[childIndex]);    }    // 设置MenuItem的布局文件,必须在setMenuItemIconsAndTexts之前调用    public void setMenuItemLayoutId(int mMenuItemLayoutId) {        this.mMenuItemLayoutId = mMenuItemLayoutId;    }    // 设置MenuItem的点击事件接口    public void setOnItemClickListener(OnItemClickListener listener) {        this.mOnMenuItemClickListener = listener;    }    // 代码省略}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94

    小民的思路大致是这样的,首先让用户通过setMenuItemIconsAndTexts函数将菜单项的图标和文本传递进来,根据这些图标和文本构建菜单项,菜单项的布局视图由mMenuItemLayoutId存储起来,这个mMenuItemLayoutId默认为circle_menu_item.xml,这个xml布局为一个ImageView显示在一个文本控件的上面。为了菜单项的可定制型,小民还添加了一个setMenuItemLayoutId函数让用户可以设置菜单项的布局,希望用户可以定制各种各样的菜单样式。在用户设置了菜单项的相关数据之后,小民会根据用户设置进来的图标和文本数量来构建、初始化相等数量的菜单项,并且将这些菜单项添加到圆形菜单CircleMenuLayout中。然后添加了一个可以设置用户点击菜单项的处理接口的setOnItemClickListener函数,使得菜单的点击事件可以被用户自定义处理。

    在将菜单项添加到CircleMenuLayout之后就是要对这些菜单项进行尺寸丈量和布局了,我们先来看丈量尺寸的代码,如下 :

        //设置布局的宽高,并策略menu item宽高    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        // 丈量自身尺寸        measureMyself(widthMeasureSpec, heightMeasureSpec);        // 丈量菜单项尺寸        measureChildViews();    }    private void measureMyself(int widthMeasureSpec, int heightMeasureSpec) {        int resWidth = 0;        int resHeight = 0;        // 根据传入的参数,分别获取测量模式和测量值        int width = MeasureSpec.getSize(widthMeasureSpec);        int widthMode = MeasureSpec.getMode(widthMeasureSpec);        int height = MeasureSpec.getSize(heightMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);        // 如果宽或者高的测量模式非精确值        if (widthMode != MeasureSpec.EXACTLY                || heightMode != MeasureSpec.EXACTLY) {            // 主要设置为背景图的高度            resWidth = getSuggestedMinimumWidth();            // 如果未设置背景图片,则设置为屏幕宽高的默认值            resWidth = resWidth == 0 ? getDefaultWidth() : resWidth;            resHeight = getSuggestedMinimumHeight();            // 如果未设置背景图片,则设置为屏幕宽高的默认值            resHeight = resHeight == 0 ? getDefaultWidth() : resHeight;        } else {            // 如果都设置为精确值,则直接取小值;            resWidth = resHeight = Math.min(width, height);        }        setMeasuredDimension(resWidth, resHeight);    }    private void measureChildViews() {        // 获得半径        mRadius = Math.max(getMeasuredWidth(), getMeasuredHeight());        // menu item数量        final int count = getChildCount();        // menu item尺寸        int childSize = (int) (mRadius * RADIO_DEFAULT_CHILD_DIMENSION);        // menu item测量模式        int childMode = MeasureSpec.EXACTLY;        // 迭代测量        for (int i = 0; i < count; i++) {            final View child = getChildAt(i);            if (child.getVisibility() == GONE) {                continue;            }            // 计算menu item的尺寸;以及和设置好的模式,去对item进行测量            int makeMeasureSpec = -1;            makeMeasureSpec = MeasureSpec.makeMeasureSpec(childSize,                    childMode);            child.measure(makeMeasureSpec, makeMeasureSpec);        }        mPadding = RADIO_PADDING_LAYOUT * mRadius;    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58

    代码比较简单,就是先测量CircleMenuLayout的尺寸,然后测量每个菜单项的尺寸。尺寸获取了之后就到了布局这一步,这也是整个圆形菜单的核心所在。代码如下 :

        // 布局menu item的位置    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        final int childCount = getChildCount();        int left, top;        // menu item 的尺寸        int itemWidth = (int) (mRadius * RADIO_DEFAULT_CHILD_DIMENSION);        // 根据menu item的个数,计算item的布局占用的角度        float angleDelay = 360 / childCount;        // 遍历所有菜单项设置它们的位置        for (int i = 0; i < childCount; i++) {            final View child = getChildAt(i);            if (child.getVisibility() == GONE) {                continue;            }            // 菜单项的起始角度            mStartAngle %= 360;            // 计算,中心点到menu item中心的距离            float distanceFromCenter = mRadius / 2f                                 - itemWidth / 2 - mPadding;            // distanceFromCenter cosa 即menu item中心点的left坐标            left = mRadius / 2 + (int)Math.round(distanceFromCenter                      * Math.cos(Math.toRadians(mStartAngle))                       * - 1 / 2f * itemWidth);            // distanceFromCenter sina 即menu item的纵坐标            top = mRadius / 2                         + (int) Math.round(distanceFromCenter                    * Math.sin( Math.toRadians(mStartAngle) )                     * - 1 / 2f * itemWidth);            // 布局child view            child.layout(left, top,                     left + itemWidth, top + itemWidth);            // 叠加尺寸            mStartAngle += angleDelay;        }    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    onLayout函数看起来稍显复杂,但它的含义就是将所有菜单项按照圆弧的形式布局。整个圆为360度,如果每个菜单项占用的角度为60度,那么第一个菜单项的角度为0~60,那么第二个菜单项的角度就是60~120,以此类推将所有菜单项按照圆形布局。首先要去计算每个菜单项的left 和 top位置 ,计算公式的图形化表示如图所示。

    上图右下角那个小圆就是我们的菜单项,那么他的left坐标就是mRadius / 2 + tmp * coas , top坐标则是mRadius / 2 + tmp * sina 。这里的tmp就是我们代码中的distanceFromCenter变量。到了这一步之后小民的第一版圆形菜单算是完成了。
    下面我们就来集成一下这个圆形菜单。
        创建一个工程之后,首先在布局xml中添加圆形菜单控件,代码如下 :

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    xmlns:tools="http://schemas.android.com/tools"    android:layout_width="match_parent"    android:layout_height="match_parent"    android:background="@drawable/bg"    android:gravity="center"    android:orientation="horizontal" ><com.dp.widgets.CircleMenuLayout        xmlns:android="http://schemas.android.com/apk/res/android"        android:id="@+id/id_menulayout"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:background="@drawable/circle_bg" /></LinearLayout>
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    为了更好的显示效果,在布局xml中我们为圆形菜单的上一层以及圆形菜单本书都添加了一个背景图。然后在MainActivity中设置菜单项数据以及点击事件等。代码如下所示 :

    public class  MainActivity extends Activity {    private CircleMenuLayout mCircleMenuLayout;    // 菜单标题    private String[] mItemTexts = new String[] {            "安全中心 ", "特色服务", "投资理财",            "转账汇款", "我的账户", "信用卡"    };    // 菜单图标    Private int[] mItemImgs = new int[] {            R.drawable.home_mbank_1_normal,            R.drawable.home_mbank_2_normal,               R.drawable.home_mbank_3_normal,            R.drawable.home_mbank_4_normal,               R.drawable.home_mbank_5_normal,            R.drawable.home_mbank_6_normal    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);            // 初始化圆形菜单             mCircleMenuLayout = (CircleMenuLayout)                 findViewById(R.id.id_menulayout);            // 设置菜单数据项            mCircleMenuLayout.setMenuItemIconsAndTexts(mItemImgs,                         mItemTexts);            // 设置菜单项点击事件            mCircleMenuLayout.setOnItemClickListener(new            OnItemClickListener() {                @Override                public void onClick(View view, int pos) {                    Toast.makeText(MainActivity.this,                             mItemTexts[pos],                        Toast.LENGTH_SHORT).show();            }        });    }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36

    运行效果如前文的动图所示。

    小民得意洋洋的蹦出了一个字:真酷!同时也为自己的学习能力感到骄傲,脸上写满了满足与自豪,感觉自己又朝高级工程师迈近了一步。

    “这不是洋叔写的圆形菜单嘛,小民也下载了?”整准备下班的主管看到这个UI效果问道。小民只好把其中的缘由、实现方式一一说给主管听,小民还特地强调了CircleMenuLayout的可定制型,通过setMenuItemLayoutId函数设置菜单项的布局id,这样菜单项的UI效果就可以被用户定制化了。主管扫视了小民的代码,似乎察觉出了什么。于是转身找来还在埋头研究代码的洋叔,并且把小民的实现简单介绍了一遍,洋叔老师在扫视了一遍代码之后就发现了其中的问题所在。

    “小民呐,你刚才说用户通过setMenuItemLayoutId函数可以设定菜单项的UI效果。那么问题来了,在你的CircleMenuLayout中默认实现的是circle_menu_item.xml的逻辑,比如加载菜单项布局之后会通过findViewById找到布局中的各个子视图,并且进行数据绑定。例如设置图标和文字,但这是针对circle_menu_item.xml这个布局的具体实现。如果用户设置菜单项布局为other_menu_item.xml,并且每个菜单项修改为就是一个Button,那么此时他必须修改CircleMenuLayout中初始化菜单项的代码。因为布局变了,菜单项里面的子View类型也变化了,菜单需要的数据也发生了变化。例如菜单项不再需要图标,只需要文字。这样一来,用户每换一种菜单样式就需要修改一次CircleMenuLayout类一次,并且设置菜单数据的接口也需要改变。这样就没有定制型可言了嘛,而且明显违反了开闭原则。反复对CircleMenuLayout进行修改不免会引入各种各样的问题……”洋叔老师果然一针见血,深刻啊!小民这才发现了问题所在,于是请教洋叔老师应该如何处理比较合适。

    “这种情况你应该使用Adapter,就像ListView中的Adapter一样,让用户来自定义菜单项的布局、解析、数据绑定等工作,你需要知道的仅仅是每个菜单项都是一个View。这样一来就将变化通过Adapter层隔离出去,你依赖的只是Adapter这个抽象。每个用户可以有不同的实现,你只需要实现圆形菜单的丈量、布局工作即可。这样就可以拥抱变化,可定制性就得到了保证。当然,你可以提供一个默认的Adapter,也就是使用你的 circle_menu_item.xml布局实现的菜单,这样没有定制需求的用户就可以使用这个默认的实现了。”小民频频点头,屡屡称是。“这确实是我之前没有考虑好,也是经验确实不足,我再好好重构一下。”小民发现问题之后也承认了自己的不足,两位前辈看小民这么好学就陪着小民一块重构代码。

    在两位前辈的指点下,经过不到五分钟重构,小民的CircleMenuLayout成了下面这样。

    // 圆形菜单public class CircleMenuLayout extends ViewGroup {    // 字段省略    // 设置Adapter    public void setAdapter(ListAdapter mAdapter) {        this.mAdapter = mAdapter;    }    // 构建菜单项    private void buildMenuItems() {        // 根据用户设置的参数,初始化menu item        for (int i = 0; i < mAdapter.getCount(); i++) {            final View itemView = mAdapter.getView(i, null, this);            final int position = i;            itemView.setOnClickListener(new OnClickListener() {                @Override                public void onClick(View v) {                    if (mOnMenuItemClickListener != null) {                        mOnMenuItemClickListener.onClick(itemView, position);                    }                }            });            // 添加view到容器中            addView(itemView);        }    }    @Override    protected void onAttachedToWindow() {        if (mAdapter != null) {            buildMenuItems();        }        super.onAttachedToWindow();    }    // 丈量、布局代码省略}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    现在的CircleMenuLayout把解析xml、初始化菜单项的具体工作移除,添加了一个Adapter,在用户设置了Adapter之后,在onAttachedToWindow函数中调用Adapter的getCount函数获取菜单项的数量,然后通过getView函数获取每个View,最后将这些菜单项的View添加到圆形菜单中,圆形菜单布局再将他们布局到特定的位置即可。

    我们看现在使用CircleMenuLayout是怎样的形式。首先定义了一个实体类MenuItem来存储菜单项图标和文本的信息,代码如下 :

    static class MenuItem {    public int imageId;    public String title;    public MenuItem(String title, int resId) {            this.title = title;         imageId = resId;   }}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    然后再实现一个Adapter,这个Adapter的类型就是ListAdapter。我们需要在getView中加载菜单项xml、绑定数据等,相关代码如下 :

    static class CircleMenuAdapter extends BaseAdapter {        List<MenuItem> mMenuItems;        public CircleMenuAdapter(List<MenuItem> menuItems) {            mMenuItems = menuItems;        }        // 加载菜单项布局,并且初始化每个菜单        @Override        public View getView(final int position, View convertView, ViewGroup parent) {            LayoutInflater mInflater = LayoutInflater.from(parent.getContext());            View itemView = mInflater.inflate(R.layout.circle_menu_item, parent, false);            initMenuItem(itemView, position);            return itemView;        }        // 初始化菜单项        private void initMenuItem(View itemView, int position) {            // 获取数据项            final MenuItem item = getItem(position);             ImageView iv = (ImageView) itemView                    .findViewById(R.id.id_circle_menu_item_image);            TextView tv = (TextView) itemView                    .findViewById(R.id.id_circle_menu_item_text);            // 数据绑定            iv.setImageResource(item.imageId);            tv.setText(item.title);        }        // 省略获取item count等代码    }
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    这与我们在ListView中使用Adapter是一致的,实现getView、getCount等函数,在getView中加载每一项的布局文件,并且绑定数据等。最终将菜单View返回,然后这个View就会被添加到CircleMenuLayout中。这一步的操作原来是放在CircleMenuLayout中的,现在被独立出来,并且通过Adapter进行了隔离。这样就将易变的部分通过Adapter抽象隔离开来,即使用户有成千上万中菜单项UI效果,那么通过Adapter就可以很容易的进行扩展、实现,而不需要每次都修改CircleMenuLayout中的代码。CircleMenuLayout布局类相当于提供了一个圆形布局抽象,至于每一个子View是啥样的它并不需要关心。通过Adapter隔离变化,拥抱变化,就是这么简单。

    “原来ListView、RecyclerView通过一个Adapter是这个原因,通过Adapter将易变的部分独立出去交给用户处理。又通过观察者模式将数据和UI解耦合,使得View与数据没有依赖,一份数据可以作用于多个UI,应对UI的易变性。原来如此!”小民最后总结道。

    例如,当我们的产品发生变化,需要将圆形菜单修改为普通的ListView样式,那么我们要做的事很简单,就是将xml布局中的CircleMenuLayout修改为ListView,然后将Adapter设置给ListView即可。代码如下 :

    public class MainActivity extends Activity {private ListView mListView;    List<MenuItem> mMenuItems = new ArrayList<MenuItem>();    @Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        // 模拟数据        mockMenuItems();        mListView = (ListView) findViewById(R.id.id_menulayout);        // 设置适配器        mListView.setAdapter(new CircleMenuAdapter(mMenuItems));        // 设置点击事件        mListView.setOnItemClickListener(new OnItemClickListener(){                @Override                public void onItemClick(AdapterView<?> parent,                     View view, int position, long id) {                             Toast.makeText(MainActivity.this,                        mMenuItems.get(position).title,                       Toast.LENGTH_SHORT).show();            }        });}
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    这样我们就完成了UI替换,成本很低,也基本不会引发其他错误。这也就是为什么我们在CircleMenuLayout中要使用ListAdapter的原因,就是为了与现有的ListView、GridView等组件进行兼容,当然我们也没有啥必要重新再定义一个Adapter类型,从此我们就可以任意修改我们的菜单Item样式了,保证了这个组件的灵活性!! 替换为ListView的效果如下所示:

    “走,我请两位前辈吃烤鱼去!”小民在重构完CircleMenuLayout之后深感收获颇多,为了报答主管和洋叔的指点嚷嚷着要请吃饭。“那就走吧!”主管倒是爽快的答应了,洋叔老师也是立马应允,三人收拾好电脑后就朝着楼下的巫山烤鱼店走去。

    20.9总结

    Adapter模式的经典实现在于将原本不兼容的接口融合在一起,使之能够很好的进行合作。但是在实际开发中,Adapter模式也有一些灵活的实现。例如ListView中的隔离变化,使得整个UI架构变得更灵活,能够拥抱变化。Adapter模式在开发中运用非常广泛,因此掌握Adapter模式是非常必要的。

     
               
    展开全文
  • 主要介绍了Adapter模式实战之重构鸿洋集团的Android圆形菜单建行的相关资料,需要的朋友可以参考下
  • 【实例简介】Android 串口通信DEMO,包括JNI CPP代码,SO库等.rar【实例截图】【核心代码】SerialUtil└── SerialUtil├── AndroidManifest.xml├── gen│ └── com│ └── example│ └── serialutil│ ...

    【实例简介】

    Android 串口通信DEMO,包括JNI CPP代码,SO库等.rar

    【实例截图】

    【核心代码】

    SerialUtil

    └── SerialUtil

    ├── AndroidManifest.xml

    ├── gen

    │   └── com

    │   └── example

    │   └── serialutil

    │   ├── BuildConfig.java

    │   └── R.java

    ├── ic_launcher-web.png

    ├── libs

    │   ├── android-support-v4.jar

    │   └── libserial_port.so

    ├── proguard-project.txt

    ├── project.properties

    ├── res

    │   ├── drawable-hdpi

    │   │   └── ic_launcher.png

    │   ├── drawable-mdpi

    │   │   └── ic_launcher.png

    │   ├── drawable-xhdpi

    │   │   └── ic_launcher.png

    │   ├── drawable-xxhdpi

    │   │   └── ic_launcher.png

    │   ├── layout

    │   │   └── activity_hello_world.xml

    │   ├── menu

    │   │   └── hello_world.xml

    │   ├── values

    │   │   ├── dimens.xml

    │   │   ├── strings.xml

    │   │   └── styles.xml

    │   ├── values-sw600dp

    │   │   └── dimens.xml

    │   ├── values-sw720dp-land

    │   │   └── dimens.xml

    │   ├── values-v11

    │   │   └── styles.xml

    │   └── values-v14

    │   └── styles.xml

    └── src

    ├── com

    │   ├── example

    │   │   └── serialutil

    │   │   └── SerialActivity.java

    │   └── skyworth

    │   └── splicing

    │   ├── SerialPort.java

    │   └── SerialPortUtil.java

    └── jni

    ├── Android.mk

    ├── com_skyworth_splicing_SerialPort.cpp

    ├── com_skyworth_splicing_SerialPort.h

    └── log.h

    25 directories, 28 files

    展开全文
  • 连接地址:直接大牛的博客
  • 问题:项目需要在android面板上开发485串口通信,读的命令发送后需要监听串口,N时间后未收到认为通信失败。使用java的FutureTask配合FileInputStream的available以及read去读响应。但是测试发现available返回num后...
  • 本文仿写自鸿洋的自定义View (一),尽管过去了将近快7年之久,我觉得依然有学习价值。 效果 自定义View分类 简单介绍一下自定义View分类: 组合控件,继承自已有的layout,比如LinearLayout,然后通过...
  • 使用的是鸿洋大神的菜单控件,在基本上添加旋转与自转。自定义菜单数量,距离中心图标距离等,可直接下载导入项目。
  • Android开发书籍推荐:从入门到精通系列学习路线书籍介绍 作者:kunga0814 很多时候我们都会不断收到新手的提问“Android开发的经典入门教材和学习路线?”、“Android 开发入门教程有哪些推荐?”等类似的问题,...
  • 原因是由于接触安卓也这么久了,但是自定义view这方面一直没怎么了解, 尤其是对于自定义属性的使用。 由于最近app上需要用到自定义View,于是决定把...首先给出鸿洋大神此博文的传送门传送门。你们可以先过去看看。
  • android适配方案过渡鸿洋_Autolayout自动化转换smallestWidth适配解决方案gradle脚本代码 如果我们的项目以前使用的是鸿洋大神的 Autolayout 现在因为这个适配方案停止维护的原因,我们期望将这个框架转换为...
  • 今天看了鸿洋_的“Android 自定义View (四) 视频音量调控”的自定义View,博客的具体地址:http://blog.csdn.net/lmj623565791/article/details/24529807#reply 刚开始自己对内切正方形的具体顶部距离的算法不是很...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,162
精华内容 464
关键字:

鸿洋android