`

Android自定义组件2转载

 
阅读更多
原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 、作者信息和本声明。否则将追究法律责任。http://vaero.blog.51cto.com/4350852/872734
<!--正文 begin-->

 Android自定义组件(一)

         在原生组件上避免不了覆写、组合等,以定义自己的组件,也方便以后复用。例如之前工程里出现了多次的文件浏览器组件。
         嗯~,该怎么总结呢?
 
一、概述
         自定义组件,大概可以这么分吧。一、View或SurfaceView上自绘;二、ViewGroup布局子类整合;三、不清楚了~,好像也没什么好分的==。
 
         本文的工程,个人觉着主要还是属性资源的使用吧?工程主要例子介绍如下:
名称
效果
属性
Loading动态...的效果组件
loading...的动态效果
定义了如下四属性:
1)loadImage:load字图片,reference类型
2)pointImage:小点图片,reference类型
3)pointCount:小点数目,integer类型
4)msecRate:毫秒级变化速率,integer类型
Title背景移位的效果组件
集合View布局,形成标题栏。实现了标题项下的背景移动的小效果。
定义了如下属性:
1)titleLayout:标题栏布局
2)bgImage:item背景图片
3)bgLeftMargin:背景初始左边距
4)animTime:移动动画时间
ViewPager绑定标题的效果组件
ViewPager绑定标题栏,并实现了标题项下的背景移动的小效果。
效果特征如下:
1)背景随ViewPager滚动而同步在标题间滚动
2)点击标题时,ViewPager程序控制滚动&背景同步
属性定义如下:
1)tLayout:标题栏布局
2)bImage:item背景图片
3)bMargin:背景初始左边距
ListView增加抽屉的效果组件
ListView增加抽屉的效果组件。抽屉打开的界面只用了一个。
1)listViewId:列表视图id,reference类型
2)drawerContent:抽屉内容视图id,reference类型
3)drawerClose:抽屉内容的关闭按钮id,reference类型
自定义能隐藏更多标题的组件
集合View布局,形成标题栏。实现超过标题数限制时,自动显示更多的效果。
初始化时,需要进行如下步骤:
1)设置显示数限制,默认将为6。
2)绑定标题内容。为String[],将直接以TextView显示==
3)绑定更多操作的视图id。将自己加载,并为其设置点击事件。
4)绑定更多显示的视图。应为已有的ViewGroup。将自动加载超出限制的标题内容(TextView)。更多操作则将控制其显示或隐藏。
另外,提供刷新内容的方法,用于:一、标题栏内容的重新加载;二,更多显示内容的重新加载。
自绘实时动态数据线
利用View绘制的实时数据显示组件?
写该文档时才挪进来的了,感觉弄得乱乱的。
双点缩放好像很不正确啊?应该是两个触摸点没弄对,获得的是一个手指头触发的两个点,所以一下放大了。(猜测,总之我是不修了^^)
 
         以下将以“ViewPager扩展组件”为例了,顺便能看下ViewPager组件^^。
 
二、步骤
         带属性资源,整合布局构建自定义组件的步骤~
 
1)attrs.xml
         定义组件需要用的属性。不用的话,就相当于一个类为一个自定义组件,不和这些东西挂钩。“自定义能隐藏更多标题的组件”即是这样的,连属性都没定义==。
 
ViewPager扩展组件定义的内容:
<!-- TitleViewPager --> 
<declare-styleable name="TitleViewPager"> 
    <attr format="reference" name="tLayout" /> 
    <attr format="reference" name="bImage" /> 
    <attr format="integer" name="bMargin" /> 
</declare-styleable> 
         format类型,参见:Android中attr自定义属性详解
 
2)item.xml
         只要是xml的resources标签内即可,单独弄出来呢最好。用以下方式定义一个id,用于View.setId(int id),主要用于相对布局时,相对于某个id的View什么的。
 
ViewPager扩展组件定义的内容:
 
  1. <item name="containerLayout" type="id"/> 
 
3)创建组件
         其类中引用属性资源。并看下ViewPager的使用说明吧:OnPageChangeListener接口方法和PagerAdapter适配器内方法的注释。
 
public class TitleViewPager extends RelativeLayout implements 
        OnPageChangeListener, View.OnClickListener { 
 
    private Context mContext; // 上下文 
    private LayoutInflater mInflater; // 布局加载器 
 
    private int titleLayoutId; // 标题栏布局id 
    private int bgImageResId; // item背景图片资源id 
    private int bgLeftMargin; // 背景初始左边距 
 
    private View titleLayout; // 标题栏布局 
    private ImageView mBgImage; // item背景图片 
    private ArrayList<View> mItemViews; // 标题项视图集合 
 
    private ViewPager mViewPager; // ViewPager组件 
    private ArrayList<View> mPageViews; // 页面视图集合 
 
    private int prevOffset = -1// 前次偏移值,这里用了int值像素 
    private int currentIndex; // 当前页面索引 
    private int previousIndex; // 前次页面索引 
    private boolean isTitleClicked; // 标题项点击 
 
    private OnPageChangeListener mOnPageChangeListener; // 页面变化监听事件 
 
    // private final int REFRESH_RATE = 20; // 刷新速率20msec 
    // private Scroller mScroller; // 滚动器 
    // private static final Interpolator sInterpolator = new Interpolator() { 
    // public float getInterpolation(float t) { 
    // t -= 1.0f; 
    // return t * t * t + 1.0f; 
    // } 
    // }; 
 
    public TitleViewPager(Context context, AttributeSet attrs) { 
        super(context, attrs); 
 
        mContext = context; 
        mInflater = (LayoutInflater) context 
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
 
        // 获得TypedArray对象 
        TypedArray typedArray = context.obtainStyledAttributes(attrs, 
                R.styleable.TitleViewPager); 
        // 获取标题栏布局id,默认0 
        titleLayoutId = typedArray.getResourceId( 
                R.styleable.TitleViewPager_tLayout, 0); 
        // 获取item背景图片资源id,默认0 
        bgImageResId = typedArray.getResourceId( 
                R.styleable.TitleViewPager_bImage, 0); 
        // 获取背景初始左边距,默认0 
        bgLeftMargin = typedArray.getInt(R.styleable.TitleViewPager_bMargin, 0); 
 
        initLayout(); // 初始化标题栏&ViewPager 
 
        mItemViews = new ArrayList<View>(); 
        mPageViews = new ArrayList<View>(); 
    } 
 
    // 初始化标题栏&ViewPager 
    private void initLayout() { 
        RelativeLayout containerLayout = new RelativeLayout(mContext); // 创建标题栏容器布局 
        containerLayout.setId(R.id.containerLayout); // 设置标识符 
        LayoutParams containerParams = new LayoutParams( 
                LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); // 宽度WRAP_CONTENT,高度WRAP_CONTENT 
        containerParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, 
                RelativeLayout.TRUE); // 贴于顶部 
        containerParams.addRule(RelativeLayout.CENTER_HORIZONTAL, 
                RelativeLayout.TRUE); // 水平居中 
        addView(containerLayout, containerParams); // 当前布局增加容器布局 
        if (0 != bgImageResId) { 
            mBgImage = new ImageView(mContext); // 创建item背景图片 
            mBgImage.setImageResource(bgImageResId); // 设置item背景图片 
            LayoutParams imageParams = new LayoutParams( 
                    LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); // 宽度WRAP_CONTENT,高度WRAP_CONTENT 
            imageParams.addRule(RelativeLayout.CENTER_VERTICAL, 
                    RelativeLayout.TRUE); // 垂直居中 
            imageParams.leftMargin = bgLeftMargin; // 左边距 
            containerLayout.addView(mBgImage, imageParams); // 标题栏容器增加标题item背景图片 
        } 
        if (titleLayoutId != 0) { 
            titleLayout = mInflater.inflate(titleLayoutId, thisfalse); // 获得标题栏布局 
            LayoutParams titleParams = new LayoutParams( 
                    LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT); // 宽度WRAP_CONTENT,高度WRAP_CONTENT 
            titleParams.addRule(RelativeLayout.CENTER_HORIZONTAL, 
                    RelativeLayout.TRUE); // 水平居中 
            titleParams.addRule(RelativeLayout.CENTER_VERTICAL, 
                    RelativeLayout.TRUE); // 垂直居中 
            containerLayout.addView(titleLayout, titleParams); // 标题栏容器增加标题栏布局 
        } 
        mViewPager = new ViewPager(mContext); // 创建ViewPager 
        mViewPager.setAdapter(new MPagerAdapter()); // 设置ViewPager适配器 
        mViewPager.setOnPageChangeListener(this); // 设置页面改变的监听接口 
        LayoutParams viewPagerParams = new LayoutParams( 
                LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT); // 宽度FILL_PARENT,高度FILL_PARENT 
        viewPagerParams.addRule(RelativeLayout.BELOW, containerLayout.getId()); // 布于标题栏容器下方 
        viewPagerParams.addRule(RelativeLayout.CENTER_HORIZONTAL, 
                RelativeLayout.TRUE); // 水平居中 
        addView(mViewPager, viewPagerParams); // 当前布局增加容器ViewPager 
    } 
 
    // 增加一个绑定页面 
    public void addBindedPage(int pageViewId, int titleItemId) { 
        mPageViews.add(mInflater.inflate(pageViewId, thisfalse)); 
        View item = titleLayout.findViewById(titleItemId); 
        item.setOnClickListener(this); 
        mItemViews.add(item); 
    } 
 
    // 获得页面数量 
    public int getCount() { 
        return mPageViews.size(); 
    } 
 
    // 初始化页面(需要在UI加载完后,可以覆写onWindowFocusChanged()) 
    public void setPage(int index) { 
        setImagePosition(index); // 设置图像至标题项位置 
        mViewPager.setCurrentItem(index, false); // 设置ViewPager当前页面 
    } 
 
    // 设置当前页面 
    public void setCurrentPage(int index, boolean isAnim) { 
        previousIndex = currentIndex; // 记录前次页面索引 
        currentIndex = index; // 设置当前页面索引 
        mViewPager.setCurrentItem(index, isAnim); // 设置ViewPager当前页面 
        // Title移动绑定在ViewPager的滚动事件内 
    } 
 
    // 设置图像至标题项位置 
    private void setImagePosition(int index) { 
        previousIndex = currentIndex; // 记录前次页面索引 
        currentIndex = index; // 设置当前页面索引 
        if (null == mBgImage) { 
            return
        } 
        LayoutParams params = (RelativeLayout.LayoutParams) mBgImage 
                .getLayoutParams(); // 获得图片布局 
        View item = mItemViews.get(index); // 标题项 
        // 注:UI加载完后getLeft()才有值 
        int targetLeftMargin = (int) (item.getLeft() + item.getWidth() / 2.0 - mBgImage 
                .getWidth() / 2.0); // 目标左边距 
        // 位置未变时直接返回,以避免多次setLayoutParams(...) 
        if (params.leftMargin == targetLeftMargin) { 
            return
        } 
        params.leftMargin = targetLeftMargin; 
        mBgImage.setLayoutParams(params); // 使设置生效 
    } 
 
    // 设置图像移动像素距离 
    private void moveImagePosition(int offset) { 
        if (null == mBgImage) { 
            return
        } 
        LayoutParams params = (RelativeLayout.LayoutParams) mBgImage 
                .getLayoutParams(); // 获得图片布局 
        params.leftMargin += offset; 
        mBgImage.setLayoutParams(params); // 使设置生效 
    } 
 
    /* 
     * 当前页滚动时调用,无论是程序控制的平滑滚动还是用户发起的触摸滚动。 
     * arg0:第一个页面当前显示的位置索引。如果页面偏移不是0,下一个页面将会可见。 
     * arg1:表示第二个页面位置偏移量的比例值,[0, 1)。(右侧页面所占屏幕百分比) 
     * arg2:表示第二个页面位置偏移量的像素值。(右侧页面距右边的像素值) 
     */ 
    @Override 
    public void onPageScrolled(int position, float positionOffset, 
            int positionOffsetPixels) { 
        // 判断是否不在动画,用positionOffsetPixels判断判断原因是: 
        // 1)position,在选中了页面时就会改变,自动动画时的不能判断 
        // 2)positionOffset,动画完变为0.0,但float不好直接等于判断 
        // 3)positionOffsetPixels,动画完变为0,int型^^ 
        if (positionOffsetPixels == 0) { 
            setImagePosition(position); 
            prevOffset = -1
            isTitleClicked = false
            return
        } 
        // 刚移动时,记录下该次值 
        if (prevOffset == -1) { 
            prevOffset = positionOffsetPixels; 
            return
        } 
        int pageOffset = positionOffsetPixels - prevOffset; // 页面偏移距离 
        prevOffset = positionOffsetPixels; 
        if (null != mBgImage) { 
            try { 
                if (pageOffset < 0) { // 左->右 
                    int prevIndex, nextIndex; 
                    if (isTitleClicked) { 
                        prevIndex = previousIndex; 
                        nextIndex = currentIndex; 
                    } else { 
                        prevIndex = currentIndex; 
                        nextIndex = currentIndex - 1
                    } 
                    // 两菜单项间的距离 
                    int itemDistance = mItemViews.get(prevIndex).getLeft() 
                            - mItemViews.get(nextIndex).getLeft(); 
                    // 图片偏移距离 
                    int imageOffset = pageOffset * itemDistance 
                            / mViewPager.getWidth(); 
                    // 设置图像移动像素距离 
                    moveImagePosition(imageOffset); 
                } else if (pageOffset > 0) { // 右->左 
                    int prevIndex, nextIndex; 
                    if (isTitleClicked) { 
                        prevIndex = previousIndex; 
                        nextIndex = currentIndex; 
                    } else { 
                        prevIndex = currentIndex; 
                        nextIndex = currentIndex + 1
                    } 
                    // 两菜单项间的距离 
                    int itemDistance = mItemViews.get(nextIndex).getLeft() 
                            - mItemViews.get(prevIndex).getLeft(); 
                    // 图片偏移距离 
                    int imageOffset = pageOffset * itemDistance 
                            / mViewPager.getWidth(); 
                    // 设置图像移动像素距离 
                    moveImagePosition(imageOffset); 
                } 
            } catch (IndexOutOfBoundsException e) { 
                // 类似在中间左右左右的来回拖拽==,判断还不够啊T^T 
                setImagePosition(currentIndex); 
                isTitleClicked = false
            } 
        } 
        if (null != mOnPageChangeListener) { 
            mOnPageChangeListener.onPageScrolled(position, positionOffset, 
                    positionOffsetPixels); 
        } 
    } 
 
    /* 
     * 当一个新页面被选中时被调用。动画不一定必须完成。 
     * arg0:新选中页面的位置索引 
     */ 
    @Override 
    public void onPageSelected(int position) { 
        if (null != mOnPageChangeListener) { 
            mOnPageChangeListener.onPageSelected(position); 
        } 
    } 
 
    /* 
     * 滚动状态改变时调用。用于发现用户何时开始拖动、页面何时自动沉降到当前页(用户不拖动时)、或者何时完全停止/空闲。 
     * arg0:新的滚动状态。SCROLL_STATE_DRAGGING、SCROLL_STATE_SETTLING、SCROLL_STATE_IDLE 
     */ 
    @Override 
    public void onPageScrollStateChanged(int state) { 
        if (state == ViewPager.SCROLL_STATE_DRAGGING) { // 用户拖动时 
            isTitleClicked = false
        } 
        if (null != mOnPageChangeListener) { 
            mOnPageChangeListener.onPageScrollStateChanged(state); 
        } 
    } 
 
    // 自定义的ViewPager适配器 
    private class MPagerAdapter extends PagerAdapter { 
 
        /* 
         * 移除一个给定位置的页面。适配器有责任从它的容器中移除视图,虽然这仅必须确认动作是在finishUpdate()后按时间完成的。 
         * arg0:容器视图,从中将移除页面。 
         * arg1:移除的页面位置 
         * arg2:和instantiateItem(View, int)返回的一样的对象 
         */ 
        @Override 
        public void destroyItem(View arg0, int arg1, Object arg2) { 
            ((ViewPager) arg0).removeView(mPageViews.get(arg1)); 
        } 
 
        /* 
         * 当显示的界面完成变化后调用。在这里,你应当必须确保所有的页面已经真正的从容器中增加或删除。 
         * arg0:容器视图,用于显示适配器的页面视图 
         */ 
        @Override 
        public void finishUpdate(View arg0) { 
        } 
 
        // 返回可用界面的数量 
        @Override 
        public int getCount() { 
            return mPageViews.size(); 
        } 
 
        /* 
         * 创建一个给定位置的界面。适配器有责任给这边给出的容器增加一个视图,虽然这仅必须确认动作是在finishUpdate()后按时间完成的。 
         * arg0:容器视图,在里面将显示页面。 
         * arg1:要被装载的页面位置 
         * Object:返回一个展现新画面的对象。这不必须是一个View,也可以是一些其他的页面容器。 
         */ 
        @Override 
        public Object instantiateItem(View arg0, int arg1) { 
            ((ViewPager) arg0).addView(mPageViews.get(arg1), 0); 
            return mPageViews.get(arg1); 
        } 
 
        // 是否是由对象生成的视图 
        @Override 
        public boolean isViewFromObject(View arg0, Object arg1) { 
            return arg0 == (arg1); 
        } 
 
        // 恢复状态 
        @Override 
        public void restoreState(Parcelable arg0, ClassLoader arg1) { 
        } 
 
        // 保存状态,返回序列化对象 
        @Override 
        public Parcelable saveState() { 
            return null
        } 
 
        /* 
         * 当显示的页面将要开始变化时调用 
         * arg0:容器视图,用于显示适配器的页面视图 
         */ 
        @Override 
        public void startUpdate(View arg0) { 
        } 
    } 
 
    @Override 
    public void onClick(View v) { 
        int size = mItemViews.size(); // 大小 
        for (int i = 0; i < size; i++) { 
            if (mItemViews.get(i).getId() == v.getId()) { 
                isTitleClicked = true
                setCurrentPage(i, true); 
                break
            } 
        } 
    } 
 
    // 获得标题项视图集合 
    public ArrayList<View> getItemViews() { 
        return mItemViews; 
    } 
 
    // 获得 页面视图集合 
    public ArrayList<View> getPageViews() { 
        return mPageViews; 
    } 
 
    // 设置页面变化监听事件 
    public void setOnPageChangeListener(OnPageChangeListener listener) { 
        mOnPageChangeListener = listener; 
    } 
 
 
分享到:
评论

相关推荐

    Android自定义组件:1、什么是自定义组件、自定义组件的方式、定义自定义属性

    最近项目中用到很多自定义组件的知识,所以想把几年前自学自定义组件的笔记整理一下和大家一起分享,也方便自己巩固。之后也会陆续把以前在公司项目当中比较经典的自定义组件案例,抽离出来和大家一起分享 二、什么...

    android设计模式详解转载

    在Android中,Adapter类是适配器模式的典型应用,它使得数据集可以绑定到UI组件,如ListView或RecyclerView。 6. 模板方法模式:定义一个操作中的算法骨架,而将一些步骤延迟到子类中。使得子类可以不改变一个算法...

    一个android游戏框架 转载.doc

    `GameSurfaceView`是游戏的核心部分,它继承自Android的`SurfaceView`,`SurfaceView`是Android系统提供用于进行高性能图形渲染的视图组件。在这个框架中,`GameSurfaceView`拥有自己的线程`GameThread`,这个线程...

    BehaviorDemo:学习自定义行为(转载https:www.jianshu.compf7989a2a3ec2)

    在Android开发中,自定义行为(Behavior)是一种高级技巧,用于扩展布局的默认行为,让开发者可以更加灵活地控制UI组件的行为。BehaviorDemo项目是为了帮助开发者深入理解和实践这一技术,特别是通过模仿UC浏览器的...

    android http server

    标题中的“android http server”指的是在Android平台上搭建一个HTTP服务器,这是为了让Android设备能够作为...开发者可以通过这样的项目学习到如何在Android设备上构建自定义服务,并与其他应用或设备进行数据交换。

    happyanimee-history_today-master_history_android源码_源码

    1. **Android开发基础**:这个项目涉及到Android应用程序的基本结构,包括Activity、Intent、BroadcastReceiver等组件的使用,以及AndroidManifest.xml配置文件的编写。 2. **网络请求**:为了获取历史事件的数据,...

    高仿点评的android应用

    2. **UI设计**:学习使用XML布局文件创建各种视图元素,如TextView、ImageView、RecyclerView等,以及自定义View和适配器以实现动态加载数据。 3. **本地数据存储**:掌握SQLite数据库的使用,包括创建表、插入、...

    cocos2d-x连连看全代码(转载)

    以上是连连看游戏基于cocos2d-x实现的关键技术点,实际项目中还可能涉及自定义组件、网络功能、广告集成等更复杂的实现。通过研究这个项目的源代码,开发者可以深入理解cocos2d-x框架及其在实际游戏开发中的应用。

    创建一个简单的widget(转载自mars)

    1. **AppWidgetProvider**: 这是Android系统用来监听和处理Widget事件的组件,类似于BroadcastReceiver。你需要创建一个继承自AppWidgetProvider的类,并重写onUpdate()、onDeleted()等方法。 2. **布局文件**: ...

    仿ipone滚轮搭配Dialog使用实例转载

    在Android中,我们可以自定义一个滚轮控件,通过滚动事件监听来实现相应的逻辑。 这个实例中的滚轮控件可能基于开源项目或者由原作者自行编写,其核心在于对触摸事件的处理和视图的绘制。通常,滚轮控件会包含以下...

    PullLoadView下拉刷新和加载更多的 recyclerView 转载 [复制链接] 评论(5)

    在Android开发中,RecyclerView是一个非常重要的组件,它用于展示可滚动的数据列表,具有高效和灵活的特性。在很多应用中,我们都需要实现下拉刷新和上拉加载更多的功能,以提供更好的用户体验。PullLoadView就是一...

    转载:ActivityGroup 实现TabHost

    随着Android系统的演进,Google引入了Fragment作为更灵活、更轻量级的组件,可以更好地支持多屏幕适配和更复杂的UI设计。 **Fragment的优势:** 1. **独立的生命周期:** Fragment有自己的生命周期方法,更容易...

    OK335xS Android用户手册V1 1 pdf

    2. **支持VK1613模块的GPS定位功能:** 同样是在Android 4.2系统中实现了对GPS定位的支持。 3. **支持USB WiFi:** 在Android 2.3和4.2系统中均支持。 4. **支持多种尺寸的触摸屏:** 包括4.3寸、5寸、7寸、8寸、...

    安卓MP3播放器修改

    6. **服务组件**:为了在后台持续播放音乐,开发者可能使用了Android服务组件(Service)。服务可以在后台运行,即使用户离开应用程序,音乐也可以继续播放。 7. **通知与远程控制**:为了方便用户在通知栏中控制...

    转载的一个侧滑删除

    3. **ListView/RecyclerView**:在Android中,`ListView`或更新的`RecyclerView`组件是实现侧滑删除的载体。开发者需要自定义`Adapter`并重写`onChildDraw()`方法来绘制滑动时的视图状态。iOS则常使用`UITableView`...

    尼斯知识体系:不积跬步无以至千里,每天进步一点点,激情,自我调节,爱与分享

    四大组件(Android 10) 文章标题 状态 原创,转载 已发布 原创 已发布 原创 已发布 原创 已发布 原创 已发布 原创 已发布 原创 Android一流层(查看) 文章标题 状态 原创,转载 已发布 原创 已发布 原创 已发布 ...

    6_安卓学生管理源码_

    8. **Android组件**:可能使用Intent进行活动间的通信,BroadcastReceiver监听系统事件,Service执行后台任务等。 9. **UI/UX设计**:遵循Material Design设计原则,使用自定义样式和主题,提高用户体验。 10. **...

    【转载记录】react-navigation安装及使用

    【React Navigation 安装与使用】 React Navigation 是一个流行的 React Native 应用程序导航库,它...通过安装和配置,我们可以轻松地实现应用程序中的页面跳转、参数传递以及自定义导航栏,从而打造出色的用户体验。

    Qt教程及软件超级浅显易懂_非常适合初学者.pdf

    它支持跨平台开发,意味着用Qt开发的应用程序可以在不同的操作系统上运行,如Windows、Linux、macOS、Android等。Qt不仅可以用来开发具有图形用户界面(GUI)的程序,还可以用来开发不依赖于GUI的程序,例如控制台...

Global site tag (gtag.js) - Google Analytics