简介除了直播之外,弹幕还能用来做什么呢?看过QQ空间的人应该都知道,QQ空间是用弹幕来预览图片的。今天,为了学习目的,我们来实现QQ空间图片预览对话框。如果你上周刚好看了我的博客,你就知道上周我写了如何实现弹幕https://www.jianshu.com/p/2b1f4da434f3
如果你注意细节,这个库就会变得非常有趣。
弹幕的手势有很多(大部分来自PhotoView),根据幻灯片高度变化的背景透明度,以及各种动画。前面我们已经讨论过弹幕如何实现,本文不再讨论。实现了弹幕,但只是直接引用弹幕库:https://github.com/mCyp/Muti-Barrage
目录一、总体理解如果我们想实现QQ空间中的图片预览,我们可以用什么来实现呢?首先,我们的基础应该是Dialog。然后您还可以使用ViewPager 在图像之间进行切换。 ViewPager2支持垂直图像切换和更好的动画过渡。那样的话你就得把整个库迁移到Android库了,但是你还能大展身手吗?剩下的工作就相对轻松了,你主要负责处理触摸事件和动画。现在我们知道ViewPager+PhotoView+Muti-BarrageView以及手势处理+动画可以形成一个简单的QQ空间般的图片预览。
1.类图我们已经知道需要使用什么技术来实现它。现在让我们看一下主要的UML 类图,以便于对以下代码进行实际解释。
如果你聪明的话,你可能已经注意到这不是代理模式。那是对的
2. 练习代码既然我们已经讨论过UML 类图,那么让我们从UML 类图顺序开始吧。
“1.IPhotoPager”
publicinterfaceIPhotoPager{voidshow();voiddismiss();voidsetConfig(Configconfig);/*config*/classConfig{Listpaths;//图片路径Listbitmaps;//BitmapbooleancanDelete=true;//普通主题booleanisShowAnimation=false;//显示是否动漫booleanisShowBarrage=true;//是否显示弹幕intanimationType;//动画类型intstartPosition=0;//图片起始位置DeleteListenerdeleteListener;//删除监听Listbarrages;//弹幕数据}}IPhotoPager 定义了必须的数据类型的基本约束使用。
“2.基本寻呼机”
publicabstractclassBasePagerextendsDialogimplementsViewPager.OnPageChangeListener,IPhotoPager{protectedContextmContext;//allbaseinfoprivateIPhotoPager.ConfigmConfig;//basicinfoprotectedintcurPosition;protectedbooleanisCanDelete;protectedbooleanisShowAnimation;protectedintanimationType;protectedDeleteListenerdeleteListener;protected booleanis ShowBarrages;protectedListbitmap s;protectedListbarrages;publicBasePager(@NonNullContextcontext){this(context,R.style.Dialog ) ; publicBasePager(@NonNullContextcontext,intthemeResId){super(context,themeResId);mContext=context;}@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);Windowwindow=getWindow();if(window!=null){window . setDimAmount (1f);}} //.省略一些ViewPager接口@OverridepublicvoidsetConfig(Configconfig){this.mConfig=config;initParams();}/*initparameter*/privatevoidinitParams(){this.isCanDelete=mConfig .canDelete ; this.isShowAnimation=mConfig.isShowAnimation;this.animationType=mConfig.animationType;this.curPosition=mConfig.startPosition;//initbitmapsthis.bitmaps=newArrayList();this.bitmaps.addAll(mConfig.bitmaps);this. mConfig .deleteListener;this.barrages=mConfig.barrages;this.isShowBarrages=mConfig.isShowBarrage;}@Overridepublicvoidshow(){if(bitmaps==null||bitmaps.size()==0){ thrownewRuntimeException(\’bitmapscan \’tbenull \’ );}super.show();//设置rect必须在dialog.showing()之后,否则dialog会在initialsize中显示。Rectrect=newRect();((Activity)mContext).getWindow().getDecorView().getWindowVisibleDisplayFrame(rect);//设置positionandsizeWindowwindow=getWindow ();WindowManager.LayoutParamslp=window.getAttributes();lp.gravity=Gravity.BOTTOM;lp.width=WindowManager.LayoutParams.MATCH_PARENT;lp.height=rect.height();window.setAttributes(lp) ;if ( isShowAnimation){if(animationType==ANIMATION_SCALE_ALPHA){window.setWindowAnimations(R.style.PhotoPagerScale);}elseif(animationType==ANIMATION_TRANSLATION){window.setWindowAnimations(R.style.PhotoPagerTranslation);}else{//defaultanimaiontistranslationwindow. setWindowAnimations(R.style.PhotoPagerAlpha);}}}}BasePager的内容也很简单,它实现了ViewPager的监听器,但是在第二步中它使用检索到的Config来填充基础数据。
《3.QQ寻呼机》
QQPager 有近400 行代码,所以我将把它分解并逐步引导您完成它。
3.1 数据初始化
数据初始化主要分为ViewPager和Muti-BarrageView初始化。作为一个快速的初始化过程,我们在这里只介绍数据。
publicclassQQPagerextendsBasePager{privatestaticfinalStringTAG=\’QQPager\’;privatestaticfinalintSCROLL_THRESHolD=100;//滑动阈值privatestaticfinalintMSG_UP=0;privateImageViewmBarrage;//弹幕切换privateMyViewPagermPhotoPager;//简单处理的ViewPager privateTextViewmPosition;//位置信息privatePhotoP gerAdaptermAdapter;//ViewPager项PhotoViewprivateBarrage ViewmBarrageView就是。 privateBarrageAdaptermBarrageAdapter; privatebooleanisInittouchSloop;//滑动阈值privatefloatlastX;//最后一个事件的坐标privatebooleanisorizontalmove=false;privatebooleanisVerticalMove=false;privatebooleanisMove=false;privateateintclickCount=0;//判断是单击还是双击。对于双击,必须传递给PhotoView进行处理privateHandlermHandler=newQQPagerHandler(this);privatestaticclassQQPagerHandlerextendsHandler{privateWeakReferencemQQPagerReference;QQPagerHandler(QQPagerqqPager){this.mQQPagerReference=newWeakReference(qqPager);}@Overridepublicvoidhandle Message(Messagemsg){ super. );switch (msg.what){caseMSG_UP:if(mQQPagerReference.get().clickCount==1)mQQPagerReference.get().dismiss();elsemQQPagerReference.get().clickCount=0;break }} }classTextViewHolderextendsBarrageAdapter.BarrageViewHolder {//.省略代码}classViewHolderextendsBarrageAdapter.BarrageViewHolder{//.省略代码}} 根据需要省略了两类弹幕持有者的一些基本数据和代码。你可以看看源代码。 QQPagerHandler的作用是检测双击。下面解释一下具体过程。
3.2 事件分发
如果您是使用过PhotoView的学生,您可能知道双击会放大图像,但既然您使用的是PhotoView,那当然是一样的,以下是直播事件时需要考虑的要点。
单击可关闭图像预览。您需要阻止发送触摸事件,Dialog 将自动处理该事件。双击必须传递给ViewPager,ViewPager 将其传递给PhotoView 进行处理。水平移动是ViewPager内部的图像切换,事件会传递给ViewPager进行处理。垂直移动意味着移动ViewPager,这是由Dialog本身处理的,ViewPager的垂直滑动距离影响背景的透明度。
话虽如此,我想你需要明白,我们只需要处理单击和双击,以及垂直和水平决策。看一下代码。
publicbooleanddispatchTouchEvent(@NonNullMotionEventev){if(ishorizontalmove)returnsuper.dispatchTouchEvent(ev);floatcurX=ev.getX();//获取当前坐标floatcurY=ev.getY();switch(ev.getAction() ){caseMotionEvent.setAlpha(1f);//Action_Down触发位置文本的显示mPosition.setVisibility(View.VISIBLE);isMove=false;clickCount++;//点击次数增加break;caseMotionEvent. curY-lastY ;if(Math.abs(deltaX)touchSloop||Math.abs(deltaY)touchSloop){isMove=true;//由于滑动距离超过阈值,点击次数会自动重置clickCount=0; } if(Math.abs(deltaX)SCROLL_THRESHolD ){scrollCloseAnimation();}else{rollbackAnimation();}}break;}returnsuper.onTouchEvent(event);} 添加很多你想要的项目的代码注释非常详细。这里的东西:
单击和双击由QQPagerHandler决定,延迟发送400毫秒。 400 毫秒内单击一次将执行关闭动画。再次点击将重置点击次数。 QQPager处理onTouchEvent时,通过getWindow().setDimamount(1f – offsetPercent)改变背景透明度。垂直移动会阻止ViewPager 事件的触发,因此当您松开手指时,该事件最终会被传递进行处理,并且如果垂直移动距离大于您设置的最小滑动阈值,则会出现滑动关闭动画。将被执行。否则,ViewPager 将回滚并移动到其初始位置。让我们看一下手势处理、双击、水平移动和垂直移动。
3.3 动画处理
预览图像需要两种类型的动画:视图动画和属性动画。有关详细信息,请参阅上面的BasePager 的show() 方法。属性动画使用的场景有位置文本的定时显示、回滚、ViewPager滑动退出。在这里我们将解释幻灯片的结尾。
privatevoidscrollCloseAnimation(){Windowwindow=getWindow();if(window!=null)window.setDimAmount(0f);if(deltaY0){mPhotoPager.animate().y(mPhotoPager.getMeasuredHeight()).setDuration(600).setListener (newSimpleAnimationListener(){@OverridepublicvoidonAnimationEnd(Animatoranimation){super.onAnimationEnd(animation);//getWindow().setWindowAnimations(R.style.PhotoPagerAlpha);dismiss();}}).start();}else{mPhotoPager. animate().y(-mPhotoPager.getMeasuredHeight()).setDuration(600).setListener(newSimpleAnimationListener(){@OverridepublicvoidonAnimationEnd(Animatoranimation){super.onAnimationEnd(animation);//getWindow().setWindowAnimations(R.style. PhotoPagerAlpha);dismiss();}}).start();}} 我不得不说,要使用属性动画,在视图本身上使用animate() 非常方便。用了很多次感觉还是不错的~
“4.PhotoPagerViewProxy”
最后,我们介绍以下主要用于构造数据的代理类:
publicclassPhotoPagerViewProxyimplementsIPhotoPager{publicstaticfinalintTYPE_NORMAL=1;publicstaticfinalintTYPE_QQ=2;publicstaticfinalintTYPE_WE_CHAT=3;publicstaticfinalintANIMATION_SCALE_ALPHA=1;publicstaticfinalintANIMATION_TRANSLATION=2;publicstaticfinalintANIMATION_ALPHA=3;privateBasePagerphotoPageView;privatePhotoPagerViewProxy(inttype,Configconfig){开关(类型){caseTYPE_QQ:photoPageView=newQQPager(context,R.style .Dialog);break;caseTYPE_WE_CHAT:break;default:photoPageView=newNormalPager(context,R.style.Dialog);break;}setConfig(config);}@Overridepublicvoidshow(){photoPageView.show();}@Overridepublicvoiddismiss(){photoPageView. ( );}@OverridepublicvoidsetConfig(Configconfig){photoPageView.setConfig(config);}publicstaticclassBuilder{privateActivitycontext;privateIPhotoPager.Configconfig;privateinttype;publicBuilder(Activitycontext,inttype){this.context=context;this.config=newIPhotoPager.Config( ) ; this.type=type;}publicBuilder(Activitycontext){//defaulttypeisTYPE_NORMALthis(context,TYPE_NORMAL);}//.另外,省略大部分代码。您需要让构建器了解的是,这是初始化数据。 mode publicPhotoPagerViewProxycreate(){returnnewPhotoPagerViewProxy(context,type,config);}}} 3.总结总的来说,代码量并不大,难度也不是很大。然而,这段代码仍然有很大的改进空间。 ViewPager垂直滑动距离变化没那么快,背景透明度增加等当然,我的水平有限,难免有错误。如有问题,欢迎指正。
就这些了~ Demo地址:https://github.com/mCyp/PhotoPagerView
Android核心知识点笔记github:https://github.com/AndroidCot/Android
本文和图片来自网络,不代表火豚游戏立场,如若侵权请联系我们删除:https://www.huotun.com/game/643970.html