This continued:
The first stop is small red book picture cutting control, depth analysis dachang cool control
Take a look at some of the renderings:
https://github.com/HpWens/MeiWidgetView welcome to Star
https://www.pgyer.com/zKF4 APK address
In the previous chapter has talked about the relevant gesture processing, this article focuses on white, list linkage effect.
Due to the length of the last article, the switching of the cutting state in the lower left corner of the picture was not explained. Through the analysis of xiaohongshu, there are the following four states:
- Cropping, change the display area of the picture. As mentioned in the previous article, the picture has any size. The default display area is rectangular area (square area) with equal width and height, while in cropping state, the display area is unequal width and height area. With the smallest edge as the base, the remaining side shrinks to three quarters of its original size, so what is the base? Here’s a simple formula to understand:
Picture width = A Picture height = BCopy the code
If a > B, it is based on width, and vice versa. Take the picture in ==demo== for example:
360 * 240
Width is the baseline, control height zoom three quarters
-
White, there is a white edge around the picture, ensure that one side of the picture is covered with controls, white edge on the other side, the size of the white edge area and the actual size of the picture.
-
Fill, with the same default state as fill, fill control, display area as a rectangular area of equal width and height (square area).
By the way, there is a point that needs to be noted that the control is scaled to three-quarters of the length in the cropping state, which is different from the small red book. The small red book changes the cropping area according to the actual size of the picture, and the minimum value is three-quarters.
Design code
cutting
MeasureSpec. GetSize () : MeasureSpec. () : MeasureSpec. () : MeasureSpec. However, the editor does not want to change the size of the control, but to change the display area of the control, in official terms, is to change the control layout area. Measurement – layout – drawing, three steps to customize view, layout related methods are as follows:
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
}
// onLayout layout is omitted. Here we override the layout method
@Override
public void layout(int l, int t, int r, int b) {
super.layout(l, t, r, b);
}
Copy the code
We can change super.layout(L, T, R, b); L, t, r, b to control the display area of the control. Notice what r, B mean here:
R = L + control width b = t + control heightCopy the code
To fill up
Full and full are the same as the default state.
White space
The area size of the white edge is related to the size of the picture. The closer the picture size ratio is to 1.0, the smaller the white edge is, and vice versa. Remember that in the previous article, to ensure that the image is fully covered with controls, the zoom value is as follows:
Math.max(Control width/picture width, Control height/picture height)Copy the code
So just make sure one side is full, just take the minimum value:
Math.min(Control width/picture width, Control height/picture height)Copy the code
Write the code
cutting
Crop cutting is divided into the following two steps:
- To determine width or height as a reference edge:
// Get the width and height of the image
Drawable drawable = getDrawable();
if (null == drawable) {
return;
}
int drawableWidth = drawable.getIntrinsicWidth();
int drawableHeight = drawable.getIntrinsicHeight();
// mIsWidthLarger True Indicates the size of the base edge. False Indicates the size of the base edge
mIsWidthLarger = drawableWidth > drawableHeight;
Copy the code
- Rewrite the Layout method to change the display width and height:
@Override
public void layout(int l, int t, int r, int b) {
if (mIsCrop && l == 0 && t == 0) {
float scaleRatio = 1.0 F;
float defaultRatio = 1.0 F;
if (mIsWidthLarger) {
// The height is 3/4 of the original height
scaleRatio = defaultRatio + defaultRatio / 4F;
} else {
// The width is 3/4 of the original width
scaleRatio = defaultRatio - defaultRatio / 4F;
}
int width = r - l;
int height = b - t;
if (scaleRatio > defaultRatio) {
int offsetY = (int) (height * (scaleRatio - defaultRatio) / 2F);
// Change the height display area except 2
t += offsetY;
b -= offsetY;
} else if (scaleRatio < defaultRatio) {
int offsetX = (int) (width * (defaultRatio - scaleRatio) / 2F);
// Change the width of the display areal += offsetX; r -= offsetX; }}super.layout(l, t, r, b);
}
Copy the code
If you don’t understand, please refer to the comments or comments. The effect picture looks like this:
White space
Fill and fill are the default states, as explained in the previous article. If you leave it white and fill it with space, the scaling of the image will change. Remember the scaling from the previous article:
Math.max(Control width/picture width, Control height/picture height)Copy the code
This ensures that the smallest edge of the image is fully covered with controls, but the effect of white space is exactly the opposite. The smallest edge of the image does not need to be fully covered with controls (white space on both sides, aligned in the center), and the non-smallest edge needs to be fully covered with controls, so the image should be scaled to a minimum, like this:
@Override
public void onGlobalLayout(a) {
// 省略......
// Image zoom ratio
mBaseScale = mIsLeaveBlank ? Math.min((float) viewWidth / drawableWidth, (float) viewHeight / drawableHeight) : Math.max((float) viewWidth / drawableWidth, (float) viewHeight / drawableHeight);
}
Copy the code
MIsLeaveBlank controls whether the parameter is left blank. True takes the minimum value. False Indicates the maximum value.
If white space changes the image display area, then the boundary detection condition of == will also change. Let’s recall that the non-white boundary detection condition is:
// boundary detection
private void boundCheck(a) {
// Get the image matrix
RectF rectF = getMatrixRectF();
if (rectF.left >= 0) {
/ / left crossing the line
}
if (rectF.top >= 0) {
/ / on the cross
}
if (rectF.right <= getWidth()) {
/ / right cross
}
if (rectF.bottom <= getHeight()) {
/ / under the cross}}Copy the code
So what are the criteria for the blank crossing? Take a look at this image first. Note the solid red lines left and right:
Red line length = (control width - image width) /2
Copy the code
To get the white space length, add the white space length to the left boundary condition:
RectF rectF = getMatrixRectF();
float rectWidth = rectF.right - rectF.left;
float rectHeight = rectF.bottom - rectF.top;
// Get the length of left and right whitespace
int leftLeaveBlankLength = (int) ((getWidth() - rectWidth) / 2);
leftLeaveBlankLength = leftLeaveBlankLength <= 0 ? 0 : leftLeaveBlankLength;
float leftBound = mIsLeaveBlank ? leftLeaveBlankLength : 0;
if (rectF.left >= 0 + leftBound) {
/ / left crossing the line
startBoundAnimator(rectF.left, 0 + leftBound, true);
}
Copy the code
The length of the white space is subtracted from the right border:
float rightBound = mIsLeaveBlank ? getWidth() - leftLeaveBlankLength : getWidth();
if (rectF.right <= rightBound) {
/ / right cross
startBoundAnimator(rectF.left, rightBound - rectWidth, true);
}
Copy the code
The case of up and down transgression is the same as the case of left and right transgression. Ok, let’s take a look at the effect:
Cache, compress, save cropped images
The cache
About LruCache introduction, Guo Lin Dgod Android DiskLruCache completely resolved, the best solution to the hard disk cache this article is still fresh. Very simple to use:
// Image cache
private LruCache<String, Bitmap> mLruCache;
// Set the maxSize as required
mLruCache = new LruCache<>(Integer.MAX_VALUE);
/ * * *@paramPath image address */
public synchronized void setImagePath(String path) {
if(path ! =null && !path.equals("")) {
Bitmap lruBitmap = mLruCache.get(path);
if (lruBitmap == null) {
// Image compression
Bitmap bitmap = BitmapUtils.getCompressBitmap(getContext(), path);
mLruCache.put(path, bitmap);
lruBitmap = bitmap;
}
if(lruBitmap ! =null) {
mFirstLayout = true;
mMaxScale = 3.0 F;
// Change the whitespace cutting state according to the actual situationsetImageBitmap(lruBitmap); onGlobalLayout(); }}}Copy the code
Clear cache:
@Override
protected void onDetachedFromWindow(a) {
// Clear the cache
if(mLruCache ! =null) { mLruCache.evictAll(); }}Copy the code
The compression
I believe that we are also familiar with the compression of pictures, here is a simple paste code:
public static Bitmap getCompressBitmap(Context context, String path) {
BitmapFactory.Options options = new BitmapFactory.Options();
// Do not load into memory
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path, options);
// Check whether it is vertical or horizontal
boolean verEnable = options.outWidth < options.outHeight;
int screenWidth = context.getResources().getDisplayMetrics().widthPixels;
int screenHeight = context.getResources().getDisplayMetrics().heightPixels;
options.inSampleSize = BitmapUtils.calculateInSampleSize(options, verEnable ? screenWidth : screenHeight, verEnable ? screenHeight : screenWidth);
options.inJustDecodeBounds = false;
return BitmapFactory.decodeFile(path, options);
}
Copy the code
Cut out pictures
Finally, we need to get the image of the control area and convert it into a Bitmap. We can use the following methods:
/ * * *@paramLeaveBlankColor The color of the blank area *@return @returnView is converted to bitmap */
public Bitmap convertToBitmap(int leaveBlankColor) {
int w = getWidth();
int h = getHeight();
Bitmap bmp = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bmp);
c.drawColor(leaveBlankColor);
layout(0.0, w, h);
draw(c);
return bmp;
}
Copy the code
In the little red book, if you cut to the selected picture again, the picture is in the state of the last operation (memory). To make it simple, the x and y axis translation and zoom ratio of the picture are the same as last operation. How to achieve this, you need to save and restore the picture position zoom ratio information.
Save:
/** * Get location information **@returnFloat [2] = {x coordinates, y coordinates} */
public float[] getLocation() {
float[] values = new float[9];
mMatrix.getValues(values);
return new float[]{values[Matrix.MTRANS_X], values[Matrix.MTRANS_Y]};
}
/ * * *@returnGet the image zoom ratio */
private float getScale(a) {
float[] values = new float[9];
mMatrix.getValues(values);
return values[Matrix.MSCALE_X];
}
Copy the code
Recovery:
/** * Restores location information **@paramX picture shifted x *@paramY picture shifted by y *@paramScale Current scale ratio of the image */
public void restoreLocation(float x, float y, float scale) {
float[] values = new float[9];
mMatrix.getValues(values);
values[Matrix.MSCALE_X] = scale;
values[Matrix.MSCALE_Y] = scale;
values[Matrix.MTRANS_X] = x;
values[Matrix.MTRANS_Y] = y;
mMatrix.setValues(values);
setImageMatrix(mMatrix);
}
Copy the code
Picture clipping control has some details, here is not a explanation, any questions, welcome to leave a message to discuss? Next, I’ll focus on list linkage.
The list of linkage
The bad old man was so bad that he wasted a lot of my time
At the beginning, I did not think of using custom view to achieve CoordinatorLayout linkage effect. Instead, I went to study CoordinatorLayout, looked up source code, breakpoint analysis, study Behavior and so on. The farther I went, the more I thought, the more complex I was, the farther I was from the truth.
There has been a thought in my heart, why the little red book can be realized, but not myself? Is the train of thought wrong? So I used the View hierarchy analysis tool again to analyze the little Red Book view hierarchy:
Custom LinearLayout to achieve list linkage effect.
The next separation effect, similar to CoordinatorLayout linkage, also has two states of expansion and closure, support == “rejection” == filing effect, in the expansion state:
XML layout hierarchy:
<LinearLayout >
<com.demo.mcropimageview.MCropImageView />
<android.support.v7.widget.RecyclerView />
</LinearLayout>
Copy the code
-
Untouched MCropImageView area, RecyclerView consumption slide event, scroll list
-
Slide up in the RecyclerView area, touch the MCropImageView area, RecyclerView and MCropImageView follow the finger to move, slide up to move out of the screen; Slide down to the screen, when the MCropImageView is fully displayed, the MCropImageView stops moving, if the finger moves to the RecyclerView area, the consumption slide event.
Stowed state:
-
Did not slide to the top of RecyclerView, RecyclerView itself consumption sliding events
-
Slide to the top of RecyclerView and slide down, RecyclerView and MCropImageView follow your finger, slide down to the screen, slide up to the screen, when the MCropImageView is completely removed from the screen, continue to slide up, Then RecyclerView consumption sliding event
Most of the time, when we’re going to do a View that follows a finger, we’re going to do either setOnTouchListener directly or we’re going to overwrite onTouchEvent directly, but that’s not appropriate for the effect we’re going to do, Because because we want it to work on any View, so if the target View has already overridden OnTouchEvent or set OnTouchListener, Sliding conflicts are very likely, and very inflexible, in which case using a custom ViewGroup is the best option. We’ve already defined the use of a custom LinearLayout to achieve the linkage effect of the list.
Design code
Linkage, linkage, so the first problem is to solve the problem of motion, how do you make the view move? Emmmm, which does not trouble me, dynamically changes the position of the view in the parent control, and provides a series of methods to animate the view:
ScrollBy, scrollTo, setTranslation, layout, offsetTopAndBottom, setScrollY and other methods. On the effect map, when the finger is lifted, the view will slide inertia according to the current sliding distance. Inertial sliding is easy with the OverScroller class.
Know how to move, then move the distance, and RecyclerView sliding, rewrite onTouchEvent to obtain the sliding offset, RecyclerView parent control to move according to the offset, when the finger is lifted, according to the offset to determine whether the parent control is expanded, fold up.
When the finger is released, the VelocityTracker will be used to obtain the sliding speed. If the speed is greater than the specified value, it will be judged as “swing”, and the Scroller will be used to carry out inertial movement, while changing the state of expansion and retraction.
If the sliding speed is lower than the specified value after the finger is released, it is regarded as “letting go”. At this time, according to whether getScrollY is greater than the specified value, the inertia movement of expansion or retraction is carried out through Scroller.
That’s about it. Let’s start coding lo
The name
How to choose a down-to-earth name? I think it is called CoordinatorLinearLayout, but also need to custom RecyclerView, we call it, CoordinatorRecyclerView. Also gave these two names a hang, ha ha, good luck.
Write the code
Create CoordinatorRecyclerView
Ok, so let’s see what CoordinatorRecyclerView says:
private int mTouchSlop = -1;
private VelocityTracker mVelocityTracker;
// Whether to re-measure for changing the height of RecyclerView
private boolean mIsAgainMeasure = true;
// Whether to expand The default value is true
private boolean mIsExpand = true;
// The maximum scrolling area of the parent class = the height of the clipping control
private int mMaxParentScrollRange;
// How far does the parent control scroll in the y direction
private int mCurrentParenScrollY = 0;
// Last RawY coordinates
private float mLastRawY = 0;
private float mDeltaRawY = 0;
// Whether to consume touch events true Consume RecyclerView cannot accept scrolling events
private boolean mIsConsumeTouchEvent = false;
// Callback interface
private OnCoordinatorListener mListener;
Copy the code
To the constructor:
public CoordinatorRecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// For handling gesture filing action
mVelocityTracker = VelocityTracker.obtain();
// Maximum sliding range = Clipping control height (clipping control width is equal to height)
mMaxParentScrollRange = context.getResources().getDisplayMetrics().widthPixels;
}
Copy the code
Through the above idea, CoordinatorRecyclerView exposes the interface method of rolling and “swinging” :
public interface OnCoordinatorListener {
/ * * *@paramY Relative to RecyclerView distance *@paramDeltaY offset *@paramMaxParentScrollRange Maximum scrolling distance */
void onScroll(float y, float deltaY, int maxParentScrollRange);
/ * * *@paramVelocityY velocity in the y direction */
void onFiling(int velocityY);
}
Copy the code
Override the onTouchEvent method:
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN:
// The corresponding code...... is omitted for space reasons
break;
case MotionEvent.ACTION_MOVE:
// y relative to RecyclerView y
float y = e.getY();
measureRecyclerHeight(y);
if (mLastRawY == 0) {
mLastRawY = e.getRawY();
}
mDeltaRawY = mLastRawY - e.getRawY();
if (mIsExpand) {
/ /
mListener.onScroll(y, mDeltaRawY, mMaxParentScrollRange);
} else {
// Suspend canScrollVertically to check whether it slides to the bottom
if(! mIsConsumeTouchEvent && ! canScrollVertically(-1)) {
mIsConsumeTouchEvent = true;
}
if(mIsConsumeTouchEvent && mDeltaRawY ! =0) { mListener.onScroll(y, mDeltaRawY, mMaxParentScrollRange); }}// Is in a non-critical state
mIsConsumeTouchEvent = mCurrentParenScrollY > 0 & mCurrentParenScrollY < mMaxParentScrollRange;
mVelocityTracker.addMovement(e);
mLastRawY = e.getRawY();
if (y < 0 || mIsConsumeTouchEvent) {
return false;
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
// Reset data
resetData();
mLastRawY = 0;
// Handle the sliding speed
mVelocityTracker.addMovement(e);
mVelocityTracker.computeCurrentVelocity(1000);
int velocityY = (int) Math.abs(mVelocityTracker.getYVelocity());
mListener.onFiling(mDeltaRawY > 0 ? -velocityY : velocityY);
mDeltaRawY = 0;
y = e.getY();
if (y < 0) {
return false;
}
break;
}
return super.onTouchEvent(e);
}
Copy the code
In the ACTION_MOVE event, retrieve the y coordinates of LLDB etY() and the offset of LLDB trawy (). In the expanded state, expose the onScroll method of the interface. In the unrolled state, determine whether to slide to the bottom and the offset is not 0. Expose interface onScroll method; Capturing the speed and direction of finger lift in ACTION_UP events exposes the onFiling interface method. Note that the return value of the onTouchEvent method, if false, RecyclerView passes down the consumption event (without sliding).
There is a detail that you have noticed. The height of RecyclerView is different in the parent class expansion and the process of refolding, as shown in the figure below. In the state of incomplete expansion, the height is green + fan area; In the fully expanded state, the height is the green area.
/ * * *@paramY finger relative RecyclerView y axis coordinates * Y <= 0 indicates that the finger has slipped out of RecyclerView top */
private void measureRecyclerHeight(float y) {
if (y <= 0 && mIsAgainMeasure) {
if (getHeight() < mMaxParentScrollRange && mIsExpand) {
mIsAgainMeasure = false; getLayoutParams().height = getHeight() + mMaxParentScrollRange; requestLayout(); }}}// Reset the height
public void resetRecyclerHeight(a) {
if(getHeight() > mMaxParentScrollRange && mIsExpand && mIsAgainMeasure) { getLayoutParams().height = getHeight() - mMaxParentScrollRange; requestLayout(); }}Copy the code
Now let’s look at the parent class CoordinatorLinearLayout.
Create CoordinatorLinearLayout
It has been mentioned above that CoordinatorLinearLayout inherits LinearLayout, which has relatively simple functions. According to the interface method exposed by CoordinatorRecyclerView, inertia sliding is carried out. Also, the member variables are first:
// Whether to expand
private boolean mIsExpand;
private OverScroller mOverScroller;
// The minimum speed of fast casting
private int mMinFlingVelocity;
// Maximum scrolling distance = height of picture clipping control
private int mScrollRange;
// Scroll the listener interface
private OnScrollListener mListener;
// Maximum expansion factor
private static final int MAX_EXPAND_FACTOR = 6;
// The scrolling duration
private static final int SCROLL_DURATION = 500;
Copy the code
Constructor, initialization of related variables:
public CoordinatorLinearLayout(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mOverScroller = new OverScroller(context);
mMinFlingVelocity = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
// Set the default value = the width of the picture cropping control
mScrollRange = context.getResources().getDisplayMetrics().widthPixels;
}
Copy the code
OnScroll methods:
/ * * *@paramY Relative to RecyclerView distance *@paramDeltaY offset *@paramMaxParentScrollRange Maximum scrolling distance */
public void onScroll(float y, float deltaY, int maxParentScrollRange) {
int scrollY = getScrollY();
int currentScrollY = (int) (scrollY + deltaY);
if(mScrollRange ! = maxParentScrollRange) { mScrollRange = maxParentScrollRange; }// Out-of-bounds detection
if (currentScrollY > maxParentScrollRange) {
currentScrollY = maxParentScrollRange;
} else if (currentScrollY < 0) {
currentScrollY = 0;
}
// Is in the expanded state
if (y <= 0) {
setScrollY(currentScrollY);
} else if (y > 0&& scrollY ! =0) { // It is in the stowed state
setScrollY(currentScrollY);
}
if(mListener ! =null) { mListener.onScroll(getScrollY()); }}Copy the code
Obtain the sliding value in the Y-axis direction first, then determine the maximum and minimum of the sliding value, and finally set the sliding value and expose the sliding value at the same time according to the unfolding and folding state.
OnFiling method:
/ * * *@paramVelocityY velocity in the y direction */
public void onFiling(int velocityY) {
int scrollY = getScrollY();
// Determine the non-critical state
if(scrollY ! =0&& scrollY ! = mScrollRange) {// Whether the y velocity is greater than the minimum throw velocity
if (Math.abs(velocityY) > mMinFlingVelocity) {
if (velocityY > mScrollRange || velocityY < -mScrollRange) {
startScroll(velocityY > mScrollRange);
} else{ collapseOrExpand(scrollY); }}else{ collapseOrExpand(scrollY); }}}Copy the code
When the finger is lifted, the sliding value of y axis direction is obtained first. In the process of expanding and refolding, the speed of Y direction returned by RecyclerView is compared with the minimum value of == “dumping” ==. If it is less than the minimum value, inertial sliding is carried out according to the sliding value; Otherwise, it is greater than the minimum value, and outside the range (mScrollRange, -mscrollrange), it is expanded and unrolled respectively. In the range, inertial sliding is also carried out according to the sliding value.
/** * expands or collapses **@param scrollY
*/
private void collapseOrExpand(int scrollY) {
// MAX_EXPAND_FACTOR = 6
int maxExpandY = mScrollRange / MAX_EXPAND_FACTOR;
if (isExpanding()) {
startScroll(scrollY < maxExpandY);
} else{ startScroll(scrollY < (mScrollRange - maxExpandY)); }}Copy the code
In the state of expansion and retract, according to whether the sliding value scrollY is greater than the specified value to control expansion and retract.
/** * start scrolling **@paramIsExpand Whether to expand */
private void startScroll(boolean isExpand) {
mIsExpand = isExpand;
if(mListener ! =null) {
mListener.isExpand(isExpand);
if (mIsExpand) {
// You must ensure that the scroll is complete before triggering the callback
postDelayed(new Runnable() {
@Override
public void run(a) { mListener.completeExpand(); } }, SCROLL_DURATION); }}if(! mOverScroller.isFinished()) { mOverScroller.abortAnimation(); }int dy = isExpand ? -getScrollY() : mScrollRange - getScrollY();
// SCROLL_DURATION = 500
mOverScroller.startScroll(0, getScrollY(), 0, dy, SCROLL_DURATION);
postInvalidate();
}
Copy the code
First, expose the isExpand interface method according to isExpand, expose the completeExpand interface method in the expanded state and when the inertial roll is completed, and then obtain the roll value according to whether the roll is expanded. Last call mOverScroller. Inertial scrolling startScroll method and rewrite computeScroll method:
@Override
public void computeScroll(a) {
// super.computeScroll();
if(mOverScroller.computeScrollOffset()) { setScrollY(mOverScroller.getCurrY()); postInvalidate(); }}Copy the code
Related interface methods are as follows:
public interface OnScrollListener {
void onScroll(int scrollY);
/ * * *@paramIsExpand Whether to expand */
void isExpand(boolean isExpand);
// Fully expanded
void completeExpand(a);
}
Copy the code
CoordinatorRecyclerView and CoordinatorLinearLayout interface are as follows:
// Implement the callback interface
mRecyclerView.setOnCoordinatorListener(new CoordinatorRecyclerView.OnCoordinatorListener() {
@Override
public void onScroll(float y, float deltaY, int maxParentScrollRange) {
mCoordinatorLayout.onScroll(y, deltaY, maxParentScrollRange);
}
@Override
public void onFiling(int velocityY) { mCoordinatorLayout.onFiling(velocityY); }}); mCoordinatorLayout.setOnScrollListener(new CoordinatorLinearLayout.OnScrollListener() {
@Override
public void onScroll(int scrollY) {
mRecyclerView.setCurrentParenScrollY(scrollY);
}
@Override
public void isExpand(boolean isExpand) {
mRecyclerView.setExpand(isExpand);
}
@Override
public void completeExpand(a) { mRecyclerView.resetRecyclerHeight(); }});Copy the code
Here, the linkage effect is almost realized, let’s have a look at the effect:
Slipped out of the RecyclerView area, the event ACTION_CANCEL was executed, causing the RecyclerView area to be clicked again, and the event Flag was reset
Now that you can get the coordinates of RecyclerView touch point, you can use the coordinates to determine which RecyclerView child view, and then call the performClick method to simulate click events:
/ * * *@param recyclerView
* @param touchX
* @param touchY
*/
public void handlerRecyclerInvalidClick(RecyclerView recyclerView, int touchX, int touchY) {
if(recyclerView ! =null && recyclerView.getChildCount() > 0) {
for (int i = 0; i < recyclerView.getChildCount(); i++) {
View childView = recyclerView.getChildAt(i);
if(childView ! =null) {
if(childView ! =null && isTouchView(touchX, touchY, childView)) {
childView.performClick();
return;
}
}
}
}
}
// Whether the touch point is in the view area
private boolean isTouchView(int touchX, int touchY, View view) {
Rect rect = new Rect();
view.getGlobalVisibleRect(rect);
return rect.contains(touchX, touchY);
}
Copy the code