技术总结-2015.10.24

这俩周在做发表的相册改版,今天总结一下其中用到的一些技术点

1、android:clipToPadding属性的妙用

属性的解释:

Defines whether the ViewGroup will clip its drawing surface so as to exclude the padding area.

定义了是否允许ViewGroup在padding中绘制。

该属性的默认值是true,即不允许。

使用场景:

1、设置ListView(或GridView等)首行或尾行距离顶部或底部有一段距离(padding),且在滑动时padding部分仍看到ListView内容的效果。

例:

android:layout_width="match_parent"

android:layout_height=“match_parent"

android:clipToPadding="false"

android:paddingTop="70dip"

android:paddingBottom="70dip"

/>

若不设置clipPadding为false,我们会直接看到ListView头部和尾部占有70dp的padding,且在滑动过程中padding始终存在,且在padding部分看不到ListView的内容。

若设置clipPadding为false,滑动过程中在padding部分可以看到ListView的内容,且有滑动到顶部才出现paddingTop 70dp,滑动到底部才出现paddingBottom 70dp的效果。

以前为了达到这个效果,解决方法可能是在首行或尾行加上一个隐藏的View,或者是加HeaderView\FooterView来实现。比较麻烦。

2、与ViewPager结合实现画廊效果

画廊效果:每次滑动只滑动一页+ 滑动item居中 + 一屏显示多个item

//缓存 3屏  左右拖动可以看到前后的图片mViewPager.setOffscreenPageLimit(3);

//设置item之间的间距

//int pageMargin = ScreenTools.instance(this).dip2px(-15);

//mViewPager.setPageMargin(pageMargin);

intpadding = ScreenTools.instance(this).dip2px(40);

mViewPager.setPadding(padding,0,padding,0);

mViewPager.setClipToPadding(false);

效果图如下:

3、网上有用android:clipChildren实现画廊效果http://www.trinea.cn/android/viewpager-multi-fragment-effect/

android:clipChildren

属性解释:

Defines whether a child is limited to draw inside of its bounds or not.

用来定义他的子控件是否要在他应有的边界内进行绘制。

默认值为true,即不允许进行扩展绘制。

2、ViewDragHelper

ViewDragHelper 是v4包中提供的一个用于解决界面控件拖拽移动问题的类,用这个类编写自定义的ViewGroup,可很大程度上简化了手势处理过于复杂的问题。自己去处理onInterceptTouchEvent和onTouchEvent这两个方法真的是一件很不容易的事

ViewDragHelper is a utility class for writing custom ViewGroups. It offers a number

of useful operations and state tracking for allowing a user to drag and reposition

views within their parent ViewGroup.

ViewDragHepler基本用法:

1、获取ViewDragHelper的实例

ViewDragHelper.create(ViewGroup forParent, float sensitivity, ViewDragHelper.Callback cb);

参数1: 一个ViewGroup, 也就是ViewDragHelper将要用来拖拽谁下面的子view

参数2:灵敏度,一般设置为1.0f就行

参数3:一个回调,用来处理拖动到位置

2、继承ViewDragHelper.Callback类,该类有个抽象方法:tryCaptureView(View view, int pointerId) 表示尝试捕获子view,这里一定要返回true, 返回true表示允许。

3、重写两个方法Callback中int clampViewPositionHorizontal(View child, int left, int dx)和intclampViewPositionVertical(View child, int left, int dx) 这两个方法分别用来处理x方向和y方向的拖动的,返回值该child现在的位置。使用时要具体处理例如防止view超出边界等。

4、重写了Callback中的onViewReleased,处理放手后的操作,例根据滑动方向及滑动距离等调用settleCapturedViewAt方法回到指定的位置,settleCapturedViewAt后要调用invalidate,因为其内部使用的是mScroller.startScroll,所以别忘了需要invalidate()以及结合computeScroll方法一起。

5、重写Callback中getViewHorizontalDragRange和getViewVerticalDragRange,只有这两个方法返回大于0view的onClick事件才能执行。(如果子View不消耗事件,那么整个手势(DOWN-MOVE*-UP)都是直接进入onTouchEvent,在onTouchEvent的DOWN的时候就确定了captureView。如果消耗事件,那么就会先走onInterceptTouchEvent方法,判断是否可以捕获,而在判断的过程中会去判断另外两个回调的方法:getViewHorizontalDragRange和getViewVerticalDragRange,只有这两个方法返回大于0的值才能正常的捕获。)

6、重写ViewGroup的onInterceptTouchEvent(MotionEvent ev)用来拦截事件

7、重写ViewGroup的onTouchEvent(MotionEvent event) 在这里面只要做两件事:mDragHelper.processTouchEvent(event);处理拦截到的事件,这个方法会在返回前分发事件;return true 表示消费了事件。

简单的几步就可以实现一个可以任意拖动到view了。

例子:

相片选择模块中底部的上下拖动抽屉效果及画廊中图片的拖动上滑删除功能都是用ViewDragHelper来实现的

效果图:画廊拖动上滑删除图片

效果图:底部上拉抽屉

3、PopupWindow setBackgroundDrawbale()

PopupWindow大家经常用,类似这样

效果:点击Popwindow外部区域可让其消失

mAlbumPopupWindow=newPopupWindow(contentView,ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.MATCH_PARENT,true);

mAlbumPopupWindow.setBackgroundDrawable(newColorDrawable(Color.TRANSPARENT));//加上这行的效果是点popupwindow的外边也能dismiss

mAlbumPopupWindow.setOutsideTouchable(true);//外部区域可点击

mAlbumPopupWindow.setTouchable(true);//window 可点击

mAlbumPopupWindow.setFocusable(true);//获得焦点 内部可点击

有个点:PopupWindow setBackgroundDrawbale() 有什么作用?

为什么去掉这行,点击外部区域,Popupwindow就不会消失呢?

解惑:

如果有背景mBackground,则会在contentView外面包一层PopupViewContainer之后作为mPopupView,如果没有背景,则直接用contentView作为mPopupView。

而这个PopupViewContainer是一个内部私有类,它继承了FrameLayout,在其中重写了Key和Touch事件的分发处理。如果不设背景,由于PopupView本身并没有重写Key和Touch事件的处理,所以如果没有包这个外层容器类,点击Back键或者外部区域是不会导致弹框消失的。

见源码:

/**

*

Prepare the popup by embedding in into a new ViewGroup if the

* background drawable is not null. If embedding is required, the layout

* parameters' height is modified to take into account the background's

* padding.

*

*@parampthe layout parameters of the popup's content view

*/

private voidpreparePopup(WindowManager.LayoutParams p) {

if(mContentView==null||mContext==null||mWindowManager==null) {

throw newIllegalStateException("You must specify a valid content view by "

+"calling setContentView() before attempting to show the popup.");

}

if(mBackground!=null) {

finalViewGroup.LayoutParams layoutParams =mContentView.getLayoutParams();

intheight = ViewGroup.LayoutParams.MATCH_PARENT;

if(layoutParams !=null&&

layoutParams.height== ViewGroup.LayoutParams.WRAP_CONTENT) {

height = ViewGroup.LayoutParams.WRAP_CONTENT;

}

// when a background is available, we embed the content view

// within another view that owns the background drawable

PopupViewContainer popupViewContainer =newPopupViewContainer(mContext);

PopupViewContainer.LayoutParams listParams =newPopupViewContainer.LayoutParams(

ViewGroup.LayoutParams.MATCH_PARENT,height

);

popupViewContainer.setBackground(mBackground);

popupViewContainer.addView(mContentView,listParams);

mPopupView= popupViewContainer;

}else{

mPopupView=mContentView;

}

mPopupView.setElevation(mElevation);

mPopupViewInitialLayoutDirectionInherited=

(mPopupView.getRawLayoutDirection() == View.LAYOUT_DIRECTION_INHERIT);

mPopupWidth= p.width;

mPopupHeight= p.height;

}

private classPopupViewContainerextendsFrameLayout {

private static finalStringTAG="PopupWindow.PopupViewContainer";

publicPopupViewContainer(Context context) {

super(context);

}

@Override

protected int[] onCreateDrawableState(intextraSpace) {

if(mAboveAnchor) {

// 1 more needed for the above anchor state

final int[] drawableState =super.onCreateDrawableState(extraSpace +1);

View.mergeDrawableStates(drawableState,ABOVE_ANCHOR_STATE_SET);

returndrawableState;

}else{

return super.onCreateDrawableState(extraSpace);

}

}

@Override

public booleandispatchKeyEvent(KeyEvent event) {

if(event.getKeyCode() == KeyEvent.KEYCODE_BACK) {

if(getKeyDispatcherState() ==null) {

return super.dispatchKeyEvent(event);

}

if(event.getAction() == KeyEvent.ACTION_DOWN

&& event.getRepeatCount() ==0) {

KeyEvent.DispatcherState state = getKeyDispatcherState();

if(state !=null) {

state.startTracking(event,this);

}

return true;

}else if(event.getAction() == KeyEvent.ACTION_UP) {

KeyEvent.DispatcherState state = getKeyDispatcherState();

if(state !=null&& state.isTracking(event) && !event.isCanceled()) {

dismiss();

return true;

}

}

return super.dispatchKeyEvent(event);

}else{

return super.dispatchKeyEvent(event);

}

}

@Override

public booleandispatchTouchEvent(MotionEvent ev) {

if(mTouchInterceptor!=null&&mTouchInterceptor.onTouch(this,ev)) {

return true;

}

return super.dispatchTouchEvent(ev);

}

@Override

public booleanonTouchEvent(MotionEvent event) {

final intx = (int) event.getX();

final inty = (int) event.getY();

if((event.getAction() == MotionEvent.ACTION_DOWN)

&& ((x <0) || (x >= getWidth()) || (y <0) || (y >= getHeight()))) {

dismiss();

return true;

}else if(event.getAction() == MotionEvent.ACTION_OUTSIDE) {

dismiss();

return true;

}else{

return super.onTouchEvent(event);

}

}

@Override

public voidsendAccessibilityEvent(inteventType) {

// clinets are interested in the content not the container, make it event source

if(mContentView!=null) {

mContentView.sendAccessibilityEvent(eventType);

}else{

super.sendAccessibilityEvent(eventType);

}

}

}

}

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 158,736评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,167评论 1 291
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,442评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,902评论 0 204
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,302评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,573评论 1 216
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,847评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,562评论 0 197
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,260评论 1 241
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,531评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,021评论 1 258
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,367评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,016评论 3 235
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,068评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,827评论 0 194
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,610评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,514评论 2 269

推荐阅读更多精彩内容