The previous two days on the micro-blog saw this side-by-side removal of the particle effect, but only iOS, so whim, wrote a play, the following simple introduction of the implementation of the idea
- Project Introduction
- Realization Principle Analysis
- Code implementation
- How to use
- More references
Project Introduction
Let's not talk nonsense.
Project Address: Https://github.com/ZhaoKaiQiang/ParticleLayout
Realization Principle Analysis
Actually saw so much about the slide-off deletion of the project, and then to think about this problem, so easy!
Let's analyze the requirements first:
-Slide gesture Detection
-Particle heel hand effect
-Delete State judgment
-Data Source Refresh
OK, we know the demand, we watch the game.
-Gesture detection can rewrite Ontouch, determine movement direction and distance
-Particle effects using third-party open source projects Leonids, the hand effect is a simple touch location update
-assuming that the sliding distance exceeds the width of item by half, it represents the delete
-Add callback interface to complete data source refresh
Code implementation
Knowing our needs and having a solution to every need, the remaining question is how to write the code.
The following section, it is best to refer to the source of this project to read ~
First of all, this is definitely a custom control, so who do we inherit from? I choose to inherit framelayout, why? Because in Framelayout we can control the mask effect.
In fact, the completion of the mask effect, there are two options,
- Simulate the matte effect by placing a layout with the same background color in framelayout and then controlling the width in Ontouch
- Directly rewrite Dispatchdraw (canvas canvas), use Canvas.cliprect () to control the drawing area, simulate matte effects
In fact, these two effects I have done, in the first version of the use of the scheme one, can accomplish this effect, but do not know how, in more than 5.0 of the system, the mask layer of the hierarchy and 5.0 inconsistencies, resulting in more than 5.0 can not be used. In addition, the use of the first effect requires more than one layer of layout, inefficient, and poor versatility, so here I choose the second scenario.
Let's start looking at the code ~
public Particlelayout (Context context) {this (context, null ); } public particlelayout (context context, AttributeSet attrs) {this (context, attrs, 0 ); } public particlelayout (context context, AttributeSet attrs, int defstyleattr) {super (c Ontext, Attrs, defstyleattr); Backlayoutrect = new Rect (); Backlocation = new int [ 2 ]; }
The constructor is very simple, in three parameter constructors, initializes two variables, Backlayoutrect is used to control the content area for subsequent touch boundary detection, backlocation is used to store layout locations, and particle effects require coordinates.
@Override protected void onsizechanged(intWintHintOLDW,intOLDH) {Super. onsizechanged (W, H, OLDW, OLDH);if(Getchildcount ()! =1) {Throw NewIllegalArgumentException ("The count of child view must is one!"); } backlayout = (ViewGroup) Getchildat (0); }@Override protected void OnLayout(BooleanChangedintLeftintTopintRightintBottom) {Super. OnLayout (changed, left, top, right, bottom); Backlayout.getlocationinwindow (backlocation); Backlayoutrect.set (backlocation[0], backlocation[1], backlocation[0] + backlayout.getmeasuredwidth (), backlocation[1] + backlayout.getmeasuredheight ()); }
The above code is very well understood, in the Onsizechange () the number of sub-view mandatory, must be one, easy to get to the content area, and in OnLayout () after the measurement, layout, Gets the location of the layout and the initialization content area Backlayoutrect.
To the current position, the initialization is complete, the following is actually the writing of touch events.
@Override PublicBooleanonintercepttouchevent(Motionevent ev) {return true; } @Override PublicBooleanontouchevent(motioneventEvent) {Switch(Event. getactionmasked ()) { CaseMotionevent.action_down:if(Event. GetX () > Backlayoutrect.width () *4/5) {Isswape =true; StartX =Event. GetX ();if(Bitmaparrays = =NULL|| Bitmaparrays.length = =0) {Particlesystem =NewParticlesystem (Activity) GetContext (), Count_of_partical_bitmap, Default_particle_bitmap, time_to_live); }Else{Random random =NewRandom ();intResId = Bitmaparrays[random.nextint (bitmaparrays.length)]; Particlesystem =NewParticlesystem (Activity) GetContext (), Count_of_partical_bitmap, ResId, time_to_live); } particlesystem.setacceleration (0.00013F -). Setspeedbycomponentsrange (0F0.3F0.05F0.3f). Setfadeout (Time_to_fade_out,NewAccelerateinterpolator ()). Emitwithgravity (Backlayout, Gravity.right, Count_of_partical_bitmap) ; } Break; CaseMotionEvent.ACTION_MOVE:clipWidth = (int) (StartX-Event. GetX ());if(Isswape && clipwidth >0) {requestlayout (); Particlesystem.updateemitverticalline (Backlayoutrect.right-clipwidth, Backlayoutrect.top-getstatubarheight (), Backlayoutrect.bottom-getstatubarheight ()); }Else{particlesystem.stopemitting (); } getParent (). Requestdisallowintercepttouchevent (true); Break; CaseMOTIONEVENT.ACTION_UP: CaseMotionEvent.ACTION_CANCEL:startX =0; Clipwidth =0; Invalidate (); Isswape =false; Particlesystem.stopemitting (); GetParent (). Requestdisallowintercepttouchevent (false);if(Event. GetX () >= getwidth ()/2) {Isdelete =false; }Else{Isdelete =true; }if(Isdelete) {if(Mdeletelistener! =NULL) {mdeletelistener.ondelete (); } } Break; }if(Isswape) {return true; }returnSuper.ontouchevent (Event); }
Although the code is a bit long, but it is not difficult, Ah, onintercepttouchevent return True, is for the region of any touch events, I have to intercept, as to where not to deal with, see ye mood ~
The core code logic is implemented in Ontouchevent (), let's take a look ~
First Action_down, if the position of the touch is 4/5 of the width, you think you want to slide, note the start of the x-coordinate startx, and then initialize the Particlesystem object below. This Particlesystem object is the main business class in the particle library Leonids, where the parameters such as particle picture, survival time, accelerated interpolator, fade time, and so on are implemented.
To the Action_move, if the moving distance of X is positive, that is, to the left, and Isswape is true, Requestlayout (), this is why? This is because if you do not requestlayout (), then the position of the layout is the position of the initialization time, resulting in the particle effect position is not correct, so recalculate the current position, and then call
particleSystem.updateEmitVerticalLine(backLayoutRect.right - clipWidth, backLayoutRect.top - getStatuBarHeight(), backLayoutRect.bottom - getStatuBarHeight());
Of course, this method in the original particle library is not, I added myself, is to be able to follow a vertical line to send particles, interested can go to see the source code.
The following code is to be in touch mode, so that the outside parent control, that is Recyclerview, will not hijack the touch event.
getParent().requestDisallowInterceptTouchEvent(true);
Finally, Action_up and Action_cancel, where the data is initialized and the deletion state is judged, and if there is a listener, it is called.
Of course, if the Isswape is true, then the touch time is consumed, otherwise, the default processing can be.
This time with the hand particles have been achieved, then the mask do?
Simple ~
@Override protectedvoiddispatchDraw(Canvas canvas) { canvas.clipRect(00, backLayoutRect.right - clipWidth, getHeight()); super.dispatchDraw(canvas); }
Before drawing the sub-view, Cliprect, so that only the layout within the scope, the mask effect is realized.
How to use
Use is also very simple, first look at the layout file
<?xml version="1.0"encoding="Utf-8"?><com. Socks. Library. ParticlelayoutXmlns:android="Http://schemas.android.com/apk/res/android"xmlns:app="Http://schemas.android.com/apk/res-auto"Android:id="@+id/root_layout"Android:layout_width="Match_parent"android:layout_height="Wrap_content"Android:background="@android: Color/white"> <android. Support. V7. Widgets. CardViewAndroid:layout_width="Match_parent"android:layout_height="Wrap_content"Android:layout_marginbottom="2DP"android:layout_marginleft="8DP"android:layout_marginright="8DP"android:layout_margintop="2DP"App:cardbackgroundcolor="@android: Color/white"app:cardcornerradius="4DP"> <textview android:id="@+id/tv"Android:layout_width="Match_parent"android:layout_height="48DP"android:gravity="Center"android:text="Test"Android:textcolor="@android: Color/primary_text_light"Android:textsize="20SP"/> </android. Support. V7. Widgets. CardView></com. Socks. Library. Particlelayout>
Just like the usual framelayout to use, and then look at the mainactivity
Public class mainactivity extends appcompatactivity { PrivateRecyclerview Recyclerview;PrivateParticleadapter Madapter;PrivateLinearlayoutmanager LayoutManager; @Overrideprotected voidOnCreate (Bundle savedinstancestate) {Super. OnCreate (Savedinstancestate); Setcontentview (R.layout.activity_main); Toolbar Toolbar = (Toolbar) Findviewbyid (R.id.toolbar); Setsupportactionbar (toolbar); Recyclerview = (Recyclerview) Findviewbyid (R.id.recyclerview); LayoutManager =NewLinearlayoutmanager ( This); Recyclerview.setlayoutmanager (LayoutManager); Madapter =NewParticleadapter (); Recyclerview.setadapter (Madapter); }Private class particleadapter extends recyclerview. Adapter<particleadapter. Particleviewholder> { PrivateArraylist<string> strings; Particleadapter () {strings =NewArraylist<> (); for(inti =0; I < -; i++) {Strings.add ("POSITION ="+ i); }} @Override PublicParticleviewholder Oncreateviewholder (viewgroup parent,intViewType) {View view = Getlayoutinflater (). Inflate (r.layout.item_layout, parent,false);return NewParticleviewholder (view); } @Override Public voidOnbindviewholder (FinalParticleviewholder Holder,Final intPosition) {Holder.tv.setText (Strings.get (position)); Holder.root_layout.setDeleteListener (NewParticlelayout.deletelistener () {@Override Public voidOnDelete () {Toast.maketext (mainactivity. This,"Detele", Toast.length_short). Show (); Strings.remove (position); madapter.notifyitemremoved (position); Madapter.notifyitemrangechanged (position, strings.size ()-position); } }); Holder.root_layout.setBitmapArrays (R.drawable.ic_star, r.drawable.ic_partical, r.drawable.ic_boom); } @Override Public intGetItemCount () {returnStrings.size (); } class particleviewholder extends recyclerview. Viewholder {TextView TV; Particlelayout root_layout; PublicParticleviewholder (View itemview) {Super(Itemview); TV = (TextView) Itemview.findviewbyid (r.id.tv); Root_layout = (particlelayout) Itemview.findviewbyid (r.id.root_layout); } } }}
The code is very simple, do not need me to say it?
The only thing to note is that after the deletion, the data source needs to be refreshed, otherwise the data at the wrong location will be deleted.
The following code is to add a particle picture, randomly displayed.
holder.root_layout.setBitmapArrays(R.drawable.ic_star, R.drawable.ic_partical, R.drawable.ic_boom);
Isn't it simple? So easy~
Goodbye ~
More references
Android Open source Particle library Leonids
Respect the original, reproduced please specify: from Kaizico (http://blog.csdn.net/zhaokaiqiang1992) infringement must investigate!
Copyright NOTICE: This article for Bo Master original article, without Bo Master permission not reproduced.
"Android Open Source project resolution" Recyclerview side-by-side delete particle effect implementation--probe into Android open source particle library Leonids