读书人

Android自定义组件二转载

发布时间: 2012-09-24 13:49:41 作者: rapoo

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,?this,?false);?//?获得标题栏布局?
????????????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,?this,?false));?
????????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

热点推荐