Android从零开搞系列:自定义View(14)仿天天美剧拖动卡片的效果(上)
2017-06-18 15:50
561 查看
转载请注意:http://blog.csdn.net/wjzj000/article/details/73432852
本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)
今天记录一篇开源项目的分析。原作者项目GitHub:https://github.com/Diolor/Swipecards
看过效果,我们来看一下项目整体,了解一下大体的结构:
我们来通过源码结构来分析一波:
红线所划掉的类是Activity,也就是这个控件的使用。因此它不属于咱们分析结构的部分。
SwipeFlingAdapterView这个类必然是我们所要使用的这个自定义View,既然结尾是AdapterView那么,这个自定义View很可能设计模式参考了同样继承了AdapterView的ListView等同类控件。
FlingCardListener必然是负责回调,这个毋庸置疑…
只是这个LinearRegression比较不好理解,那么就让咱们在分析代码的时候去好好理解一番!
通过它的用法,我们可以看到和ListView的用法基本类似,和我们开头分析的一样。
那么就让我们看一看这个自定义View的setAdapter是如何处理的。
AdapterDataSetObserver:
接下来让我们看一看比较核心的方法,重点的地方已经直接写在了注释之中。
走到这,我们的这个自定义View通过setAdapter就可以让Adapter中的数据进行显示了。当然这里正常显示还是需要自定义View中的其他代码进行辅助。接下来就让我们进一步展开这个自定义View的其他内容。
OK,到这里setAdapter这条线我们就已经分析出来了。但是这里仅仅是显示多个Card而已,并没有像效果当中的拖动,因此想要拖动的话,势必要存在onTouch事件。而这里我们则需要追溯到FlingCardListener这个类当中,由于篇幅比较长,关于
本篇下:http://blog.csdn.net/wjzj000/article/details/73441173
https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp
本菜开源的一个自己写的Demo,希望能给Androider们有所帮助,水平有限,见谅见谅…
https://github.com/zhiaixinyang/PersonalCollect (拆解GitHub上的优秀框架于一体,全部拆离不含任何额外的库导入)
https://github.com/zhiaixinyang/MyFirstApp(Retrofit+RxJava+MVP)
写在前面
一晃大三即将结束,这学期真是出奇的快。快到还没感受春天的清香,就进入了热出翔的夏天…mmp,真的热,热到翻个身一身汗….不过,好在学习使我快乐,学习使我快乐,学习使我快乐!今天记录一篇开源项目的分析。原作者项目GitHub:https://github.com/Diolor/Swipecards
开始
开始之前让我们先瞅一下效果:看过效果,我们来看一下项目整体,了解一下大体的结构:
我们来通过源码结构来分析一波:
红线所划掉的类是Activity,也就是这个控件的使用。因此它不属于咱们分析结构的部分。
SwipeFlingAdapterView这个类必然是我们所要使用的这个自定义View,既然结尾是AdapterView那么,这个自定义View很可能设计模式参考了同样继承了AdapterView的ListView等同类控件。
FlingCardListener必然是负责回调,这个毋庸置疑…
只是这个LinearRegression比较不好理解,那么就让咱们在分析代码的时候去好好理解一番!
使用方式
swipeCard.setAdapter(arrayAdapter); swipeCard.setFlingListener(new SwipeFlingAdapterView.onFlingListener() { //重写相应的回调方法 }
通过它的用法,我们可以看到和ListView的用法基本类似,和我们开头分析的一样。
那么就让我们看一看这个自定义View的setAdapter是如何处理的。
SwipeFlingAdapterView
setAdapter方法( ):
如果我们看了ListView的setAdapter方法,我们会发现二者思路基本相同,无非了考虑的情况精细与否。@Override public void setAdapter(Adapter adapter) { if (mAdapter != null && mDataSetObserver != null) { mAdapter.unregisterDataSetObserver(mDataSetObserver); mDataSetObserver = null; } mAdapter = adapter; if (mAdapter != null && mDataSetObserver == null) { //简单的继承DataSetObserver mDataSetObserver = new AdapterDataSetObserver(); mAdapter.registerDataSetObserver(mDataSetObserver); } }
AdapterDataSetObserver:
private class AdapterDataSetObserver extends DataSetObserver { @Override public void onChanged() { requestLayout(); } @Override public void onInvalidated() { requestLayout(); } }
接下来让我们看一看比较核心的方法,重点的地方已经直接写在了注释之中。
onLayout( )方法:
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (mAdapter == null) { return; } mInLayout = true; final int adapterCount = mAdapter.getCount(); //如果adapter中没有对象,清空View if(adapterCount == 0) { removeAllViewsInLayout(); }else { //获取最上层的Card,LAST_OBJECT_IN_STACK初始化为0 View topCard = getChildAt(LAST_OBJECT_IN_STACK); if(mActiveCard!=null && topCard!=null && topCard==mActiveCard) { if (this.flingCardListener.isTouching()) { //此处通过Listener的getLastPoint()拿到Card的坐标信息 PointF lastPoint = this.flingCardListener.getLastPoint(); //第一次时mLastTouchPoint必然为null,此时进行拖动赋值 if (this.mLastTouchPoint == null || !this.mLastTouchPoint.equals(lastPoint)) { this.mLastTouchPoint = lastPoint; //移除第一个Card,加载下面的Card removeViewsInLayout(0, LAST_OBJECT_IN_STACK); //加载下面的Card ( 此方法具体内容在下面展开 ) layoutChildren(1, adapterCount); } } }else{ //重置UI并设置顶视图监听器 removeAllViewsInLayout(); layoutChildren(0, adapterCount); // ( 此方法具体内容在下面展开 ) setTopView(); } }
layoutChildren( )方法:
private void layoutChildren(int startingIndex, int adapterCount){ while (startingIndex < Math.min(adapterCount, MAX_VISIBLE) ) { //获取移除后的下一个Card View newUnderChild = mAdapter.getView(startingIndex, null, this); if (newUnderChild.getVisibility() != GONE) { //显示移除后的下一个Card ( 此方法具体内容在下面展开 ) makeAndAddView(newUnderChild); //LAST_OBJECT_IN_STACK赋值成当前的Card的索引 LAST_OBJECT_IN_STACK = startingIndex; } //自增 startingIndex++; } }
makeAndAddView( )方法:
private void makeAndAddView(View child) { FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) child.getLayoutParams(); /** * 将child加入到ViewGroup中 * true:则调用此方法将不会触发子对象的布局请求 * * 想要Child被绘制出来,需要调用Child的layout() */ addViewInLayout(child, 0, lp, true); //指示在下一层次布局传递期间是否请求此视图的布局 final boolean needToMeasure = child.isLayoutRequested(); //对View进行测量 if (needToMeasure) { int childWidthSpec = getChildMeasureSpec(getWidthMeasureSpec(), getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, lp.width); int childHeightSpec = getChildMeasureSpec(getHeightMeasureSpec(), getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin, lp.height); child.measure(childWidthSpec, childHeightSpec); } else { cleanupLayoutState(child); } int w = child.getMeasuredWidth(); int h = child.getMeasuredHeight(); int gravity = lp.gravity; //对Gravity的情况进行处理 if (gravity == -1) { gravity = Gravity.TOP | Gravity.START; } int layoutDirection = getLayoutDirection(); final int absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection); final int verticalGravity = gravity & Gravity.VERTICAL_GRAVITY_MASK; int childLeft; int childTop; //通过不同的Gravity模式,计算的Card位置 switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { case Gravity.CENTER_HORIZONTAL: childLeft = (getWidth() + getPaddingLeft() - getPaddingRight() - w) / 2 + lp.leftMargin - lp.rightMargin; break; case Gravity.END: childLeft = getWidth() + getPaddingRight() - w - lp.rightMargin; break; case Gravity.START: default: childLeft = getPaddingLeft() + lp.leftMargin; break; } switch (verticalGravity) { case Gravity.CENTER_VERTICAL: childTop = (getHeight() + getPaddingTop() - getPaddingBottom() - h) / 2 + lp.topMargin - lp.bottomMargin; break; case Gravity.BOTTOM: childTop = getHeight() - getPaddingBottom() - h - lp.bottomMargin; break; case Gravity.TOP: default: childTop = getPaddingTop() + lp.topMargin; break; } //设置Card的位置,不调用此方法addViewInLayout()无效果 child.layout(childLeft, childTop, childLeft + w, childTop + h); }
走到这,我们的这个自定义View通过setAdapter就可以让Adapter中的数据进行显示了。当然这里正常显示还是需要自定义View中的其他代码进行辅助。接下来就让我们进一步展开这个自定义View的其他内容。
setTopView()方法:
设置第一个Card的显示,并且设置监听private void setTopView() { if(getChildCount()>0){ //拿到第一个Card mActiveCard = getChildAt(LAST_OBJECT_IN_STACK); if(mActiveCard!=null) { //移动Card的监听,具体展开在下边 flingCardListener = new FlingCardListener(mActiveCard, mAdapter.getItem(0), ROTATION_DEGREES, new FlingCardListener.FlingListener() { @Override public void onCardExited() { mActiveCard = null; mFlingListener.removeFirstObjectInAdapter(); } //从左边离开回调 @Override public void leftExit(Object dataObject) { mFlingListener.onLeftCardExit(dataObject); } //从右边离开回调 @Override public void rightExit(Object dataObject) { mFlingListener.onRightCardExit(dataObject); } //点击回调 @Override public void onClick(Object dataObject) { if(mOnItemClickListener!=null) mOnItemClickListener.onItemClicked(0, dataObject); } //手指移动不松开的回调 @Override public void onScroll(float scrollProgressPercent) { mFlingListener.onScroll(scrollProgressPercent); } }); mActiveCard.setOnTouchListener(flingCardListener); } } }
OK,到这里setAdapter这条线我们就已经分析出来了。但是这里仅仅是显示多个Card而已,并没有像效果当中的拖动,因此想要拖动的话,势必要存在onTouch事件。而这里我们则需要追溯到FlingCardListener这个类当中,由于篇幅比较长,关于
FlingCardListener这个类的展开放到了下一篇博客当中。
本篇下:http://blog.csdn.net/wjzj000/article/details/73441173
尾声
希望各位看官可以star我的GitHub,三叩九拜,满地打滚求star:https://github.com/zhiaixinyang/PersonalCollect
https://github.com/zhiaixinyang/MyFirstApp
相关文章推荐
- Android从零开搞系列:自定义View(15)仿天天美剧拖动卡片的效果(下)
- android自定义View基础系列一(点击随机生成验证码效果)
- Android自定义View圆形和拖动圆、跟随手指拖动效果
- Android 自定义View 实现QQ红点拖动删除效果
- Android自定义组件系列【14】——Android5.0按钮波纹效果实现
- Android自定义组件系列【14】——Android5.0按钮波纹效果实现
- Android自定义控件系列六:自定义ViewGroup(一)实现ViewPager效果
- Android从零开搞系列:自定义View(16)自定义验证码输入框效果
- Android从零开搞系列:自定义View(13)新消息小圆点效果
- Android自定义view系列之99.99%实现QQ侧滑删除效果实例代码详解
- Android自定义控件系列六:自定义ViewGroup(一)实现ViewPager效果
- Android自定义控件系列六:自定义ViewGroup(一)实现ViewPager效果
- Android自定义控件系列五:自定义ViewGroup(一)实现ViewPager效果
- Android自定义View实现转盘旋转的效果
- Android自定义View实现HTML图文环绕效果
- Android中如何使用ViewPager实现类似laucher左右拖动效果 3ff8
- Android中如何使用ViewPager实现类似laucher左右拖动效果
- Android学习备忘020——android自定义ImageView实现缩放,回弹效果
- Android自定义View实现HTML图文环绕效果
- Android自定义View实现HTML图文环绕效果