Interactive Implementation of High Imitation Pinterest

Label of this article: Pinterest can't be opened High imitation Pinterest Jianwoo

Pinterest There is a very good interaction, probably the operation is that the list page can be long by a single Item to say Item selected and make the surrounding items transparent and white, and then pop up the option menu, you can choose the menu through the movement of your fingers, let's see a graph, compare the implementation of the simplified and Pinterest Effectiveness

High imitation Pinterest interaction

On the right is Pinterest, on the left is Jianwoo. The red icon is the theme red of my app. It's a little different from Interest. Other imitations are as follows Pinterest There's not much difference. Here's an interactive animation in Jianwoo.

Simplified Pinterest Interaction

Is the interaction great? Next, let's discuss the specific implementation ideas, what problems we will encounter and how to solve them.
Think is the most important thing!

Analysis

What are the effects to be achieved?

  • 1. After pressing item for a long time, pop up the menu
  • 2. After popping up the menu, you can select the menu by touching the screen.
  • 3. When the menu arrives at the selection area, the icon should be animated and the title text of the current menu should be displayed.
  • 4. Except for the selected area, it should turn white.
  • 5. There are exit animations in Title bar and Tab bar after long press, and animations back after release.

Now that we have identified the problem, let's solve it.

Long press Item and pop-up menu

Question: After a long press, the menu pops up. Where does the menu appear? (What should the container control of the menu be) is it on the current Fragment/Activity?
This is the first question. To pop up the menu, the menu can not appear empty. First, we need to determine the container that carries it and add it with the current Activity or Fragment's root layout. OK? Okay, but should we do that? Is that good?
Imagine: If the pop-up menu is above the current Activity/Fragment layout, it means that your Activity/Fragment layout has to adapt to the interaction. LineaLayout can't be the root layout of Activity/Fragment, and most importantly, once you decide to add these View elements with Activity/Fragment layout, it means you. This interaction is bound to Activeness / Fragment, and reuse will be very cumbersome! ____________ This is a plan that should never be adopted. It's wrong at first, and it's going to be wrong a lot later.
So what should we use? I first thought about Windows Manager. Why? Because Windows Manager doesn't need Activity as the host, it's free and convenient. By setting Windows Manager. LayoutParams. type reasonably, it can also make the pop-up suspension window do not need permission. It's nice, but there's a century-old bug: if the user sets the suspension window not allowed to pop-up, it will mean that Windows Manager can't use it. In this case, Windows Manager can't use it. Even Toast can't be used! It has to be said that a manufacturer castrates for users, so I implemented the implementation of Dialog, but in fact, whether using WIndowManger or Dialog, it does not affect you to achieve the main functions, because Windows Manager/Dialog only needs you to provide a View to it, and Dialog needs a host activity.

Now that we have identified the container where the pop-up View appears, let's go ahead and solve the next problem. How do we locate the pop-up menu?
This is a long-click pop-up menu. First, set OnTouchListener to the Item view. By listening to ACTION_DOWN, you can get the coordinates x,y in the screen pressed by the user.
stay Adapter in

    getHolder(holder).mImage.setOnTouchListener(listener);
    public TouchToSelector onTouch(View v, MotionEvent event){
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                /**
                 * Get the x,y coordinates pressed
                 */
                setTouchX(event.getRawX());
                setTouchY(event.getRawY());
                /**
                 * Record Press Time
                 */
                setDownTime(System.currentTimeMillis());
                create(v);
                break;
            case MotionEvent.ACTION_CANCEL:
                ....
                break;
            case MotionEvent.ACTION_UP:
                ....
                break;
        }
        return this;
    }

Notice that you get the X of an Item listening event on the screen. The y coordinate is getRawX () & getRawY ().
Now that we have got the coordinates we pressed, we can calculate the position of the pop-up menu. The strategy of icon appearance in Pinterest is not only that icons appear around the finger, but also that the direction of icons is determined according to the X and y of the coordinates pressed. That is, when the finger is on the top of the screen, the icon is down, and when the finger is on the right side of the screen, the icon is left. Yes, that is.

  • 1. Divide the screen into four areas, determine the area by x,y coordinates, and determine the direction of the icon.
  • 2. According to the direction of the icon and the coordinates of X and y, the coordinates of the central point of the icon can be calculated based on the distance angle of 45 degrees between each icon. Here we can use either quadratic function or trigonometric function. I use trigonometric function here.

Step 1: Get the quadrant area by pressing the x,y coordinates on the screen (which is not in the same order as the mathematical quadrant).

    /**
     * Press the x,y coordinates on the screen
     * getScreenWidth() Screen width
     * getScreenHeight() Screen Height
     * @param x x coordinate
     * @param y y coordinate
     * @return
     */
    private int getQuadrant(int x, int y){
        /**
         * First region
         */
        if(x < getScreenWidth() / 2 && y < getScreenHeight() / 2){
            return 1;
        }
        /**
         * Second region
         */
        if(x < getScreenWidth() && x > getScreenWidth() / 2 && y < getScreenHeight() / 2){
            return 2;
        }
        /**
         * Third region
         */
        if(x < getScreenWidth() && x > getScreenWidth() / 2 && y > getScreenHeight() / 2 && y < getScreenHeight()){
            return 3;
        }
        /**
         * Region IV
         */
        if(x < getScreenWidth() / 2 && y > getScreenHeight() / 2 && y < getScreenHeight()){
            return 4;
        }
        return 3;
    }

When we get the quadrant area, we can know the position of icon and the angle formed by pressing the point. Through the angle and the length of the distance, we can calculate the X and Y coordinates of icon. Then we can make icon appear in the designated position by setting the Layout Params of icon (the relevant code below is not in the same class).

    /**
     * The right-angled edge width of the icon position and the x-axis of the triangle formed by pressing the down point and the x-axis
     */
    protected int mWidth;
    /**
     * The icon position and the right-angled edge height where y forms a triangle by pressing the down point and the x-axis
     */
    protected int mHeight;
    /**
     * The top left corner x where the icon is located
     */
    protected int mIndexX;
    /**
     * The top left corner y where the icon is located
     */
    protected int mIndexY;

    public void initParams(){
        setVisibility(View.GONE);
        mBaseDegree = 0;
        setItemId(mITouchView.getItemId());
        mNormalResId = mITouchView.getImageResNormal();
        mPressResId = mITouchView.getImageResPress();
        mTitle = mITouchView.getImageTitle();
        mIndex = mITouchView.getImageIndex();
        setImageResource(getNormalResId());
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(getIconWidth(),getIconHeight());
        /**
         * high
         */
        mHeight = Math.abs((int)(Math.sin(getAngle(getAngle())) * getR()));
        /**
         * wide
         */
        mWidth = Math.abs((int)(Math.cos(getAngle(getAngle())) * getR()));

        mIndexX = getTouchX() - mWidth - getIconWidth() / 2;
        mIndexY = getTouchY() - mHeight - getIconHeight() / 2 - getStatusBarHeight();
        params.setMargins(getIndexX(), getIndexY(), 0, 0);
        setLayoutParams(params);
    }

    /**
     * Determine the angle of the quadrant region according to the index order
     * getFactor() The angle between the icon and the coordinate line is 45 degrees here.
     * getSingleAngle()The final return is the basic angle calculated from the quadrant.
     * @return
     */
    public int getAngle(){
        return getIndex() * getFactor() + getSingleAngle();
    }

    /**
     * Calculate the true angle according to the angle of input (for trigonometric function calculation in Math)
     * @param angle
     * @return
     */
    public double getAngle(int angle){
        return angle * Math.PI / 180;
    }

    /**
     * Angle of two icons
     * @return
     */
    public int getSingleAngle(){
        return getBaseDegree();
    }

    public int getBaseDegree() {
        switch (getQuadrant(getTouchX(), getTouchY())){
            case 1:
                return mBaseDegree + 135;
            case 2:
                return mBaseDegree + 225;
            case 3:
                return mBaseDegree + 0;
            case 4:
                return mBaseDegree + 45;
        }
        int degree = 180 - (1 - getQuadrant(getTouchX(), getTouchY())) * 90;
        degree = degree > 360 ? degree - 360 : degree;
        return degree + mBaseDegree;
    }

Knowing the x,y coordinates of the icon and the angle of the icon, we can now start making an animation that appears and disappears.

    public void show(){
        setVisibility(View.VISIBLE);
        invalidate();
        TranslateAnimation animation = new TranslateAnimation(getShowAndHideXY()[0], 0, getShowAndHideXY()[1], 0);
        animation.setDuration(200);
        animation.setStartOffset(TouchToSelecttorConfig.ANIMATION_START_OFFSET);
        animation.setInterpolator(new LinearOutSlowInInterpolator());
        startAnimation(animation);
    }

    public void hide(){
        TranslateAnimation animation = new TranslateAnimation(0, getShowAndHideXY()[0], 0, getShowAndHideXY()[1]);
        animation.setDuration(200);
        animation.setInterpolator(new LinearOutSlowInInterpolator());
        animation.setFillAfter(true);
        startAnimation(animation);
    }

     /**
     * getW() The right-angled edge width of the icon position and the x-axis of the triangle formed by pressing the down point and the x-axis
     * getH() The icon position and the right-angled edge height where y forms a triangle by pressing the down point and the x-axis
     * fromX and fromY for Mobile Animation when Pressed
     * @return
     */
    private int[] getShowAndHideXY(){
        int[] showAndHideXY = new int[2];
        switch (getQuadrant(getTouchX(), getTouchY())){
            case 1:
                showAndHideXY[0] = getW() * -1;
                showAndHideXY[1] = getH() * -1;
                break;
            case 2:
                showAndHideXY[0] = getW();
                showAndHideXY[1] = getH() * -1;
                break;
            case 3:
                showAndHideXY[0] = getW();
                showAndHideXY[1] = getH();
                break;
            case 4:
                showAndHideXY[0] = getW() * -1;
                showAndHideXY[1] = getH();
                break;
        }
        return showAndHideXY;
    }

The location of the event and the disappearance of the animation are all solved, so here's how to diffuse the touch event to the entire screen after setting up OnTouchListener in the Adapter Item view.
We just set up an OnTouchListener for Adapter Item view

getHolder(holder).mImage.setOnTouchListener(listener);

But this monitoring area is only a large area of view, and the pop-up window may be either Windows Manager or Dialog, that is, it is not at the same view level as Activity. So how can we make the pop-up view receive our onTouch event?
We can't distribute the onTouch event of view to Windows Manager/Dialog, but we can distribute the event to the Content of Activity or ScrollView, a sub-container of the current Fragment. The pop-up view only needs to touch the X and Y coordinates. We just pass the X and Y coordinates received after distribution to the pop-up view.

This involves the event distribution mechanism of View, such as the nesting of the following containers at the interface level: Activity Container A, Fragment Container B, ScrollView Container C, Recyclerview Container D, Adapter View E. If you know Android's event distribution mechanism, you will know that the order of event distribution is A-> B-> C-> D-> E, if there is a container in the middle. If you rewrite onTouchEvent or set onTouchListener and set the return value to true, the current level ViewGroup will consume the event and will not distribute it downward. Of course, if you set onTouchEvent to return true to intercept the event and block the event downward distribution, then there is no need for parent container to intercept the event here.
Because it's not a sliding conflict itself, we just need to get a larger parent container of the current View's operating area and set it an onTouchListener listener with a true return value to consume the event and not distribute it downwards. That's the point.
So what do we do?
1. Because we only pop up the menu after a long press, that is to say, the requirement should be to pop up the menu before starting to set up onTouchListener for the parent container. This is done by ACTION_DOWN in the onTouch method of the Adapter view. How to deal with it? We can turn on a timer, which waits a long time for us to press, and when it's time, we can start the next operation.

    public TouchToSelector onTouch(View v, MotionEvent event){
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:
                /**
                 * Get the x,y coordinates pressed
                 */
                setTouchX(event.getRawX());
                setTouchY(event.getRawY());
                /**
                 * Record Press Time
                 */
                setDownTime(System.currentTimeMillis());
                create(v);
                break;
            case MotionEvent.ACTION_CANCEL:
                ...
                break;
            case MotionEvent.ACTION_UP:
                ....
                break;
        }
        return this;
    }

    private TouchToSelector create(View view){
        this.mView = view;
        this.mContext = view.getContext();
        setCanLongClick(true);
        initTimer();
        return this;
    }

    private void initTimer(){
        mTimer = new Timer();
        TimerTask task = new TimerTask() {
            @Override
            public void run() {
                if(isCanLongClick()){
                    mHandler.sendEmptyMessage(LONG_CLICK);
                }
            }
        };
        mTimer.schedule(task, TouchToSelecttorConfig.LONG_CLICK);
    }

This is the implementation and process of long-time, so after we receive the message of timer arrival, we should get the parent container and set up onTouchListener, consume the event and do not distribute it downwards, so the handleMessage method does not write.

   private void handleOnLongClick(){
        dispatchTouchEvent();
        initWindowManager();
        initBg();
        dispatchEvent();
        if(null != onLongClickListener){
            onLongClickListener.onLongClick(mView);
        }
    }

    private void dispatchTouchEvent(){
        getScrollView().setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                if(null != mBg){
                    mBg.onTouchEvent(event);
                }
                switch (event.getAction()){
                    case MotionEvent.ACTION_CANCEL:
                    case MotionEvent.ACTION_UP:
                        return getScrollView().onTouchEvent(event);
                }
                return true;
            }
        });
    }

    private ViewGroup getScrollView(){
        return (ViewGroup) findViewById(mScrollViewId != 0 ? mScrollViewId : android.R.id.content);
    }

Notice that I passed the event to mBg in onTouch listening. What is mBg? It's the parent container of our pop-up menu. We just need to make coordinate judgment in mBg to know which icon we touched. The following code excerpts mBg. onTouchEvent () - > ACTION_MOVE and the method inside the icon ImageView to judge by distance and angle. Which icon is the response selected, and the selected icon returns the result by listening

    public void handleActionMove(MotionEvent event){
        for(TouchToSelectorImageView touchToSelectorImageView:mTouchToSelectorImageViews){
            touchToSelectorImageView.handleActionMove(event);
        }
    }
    public void handleActionMove(MotionEvent event){
        if(!isInDistance(event)){
            if(mScaleToBig){
                hideTitle();
                handleItemUnSelect();
            }
        }
        if(isInDistance(event) && isInAngle(event)){
            scaleToBig();
        }else {
            scaleToSmall();
        }
    }

    private boolean isInDistance(MotionEvent event){
        int distance = (int)getTwoPointDistance((int)event.getRawX(), (int)event.getRawY(), getTouchX(), getTouchY() + getStatusBarHeight());
        return distance > getR() / 3  && distance < (getR() + getR() / 3);
    }

    /**
     * Obtain the angle between the line formed by the point pressed and the moving point and the X-axis.
     * @return
     */
    private boolean isInAngle(MotionEvent event){
        int x1 = (int)event.getRawX();
        int y1 = (int)event.getRawY();
        int degree = getTwoPointDegreeDiffTouchXY(x1, y1);
        int minDegree = getMinDegree();
        int maxDegree = getMaxDegree();
        return degree > minDegree && degree < maxDegree;
    }

Now that touching and judging whether the icon is selected has also been solved, there is still a problem, that is, how the peripheral whitening is achieved. Let's look back at the picture below.

High imitation Pinterest interaction


Apart from ITEM's Layout, all the other areas are white. How to achieve this is not difficult.

  • 1. Set the background of the pop-up View container to be transparent
  • 2. Add a View to the background, which is cut into an area, that is, "Select the area of item"
  • 3. How to cut it? Path is used here. Path is used to draw the path, and then the View is painted, that is, in the draw method.
canvas.drawPath(path, paint);

How to draw this path? There are many ways to draw paths. Ultimately, you just need to separate the item area, but here's a point: press the position x,y is not the coordinates of the upper left corner of layout view, get the coordinates of view in the screen, using the following method

    int[] location = new int[2];
    view.getLocationOnScreen(location);

I will not elaborate on the code for drawing paths. Here I paste out the code for drawing paths, and I will not describe it.

    public void draw(Canvas canvas) {
        final int x = getViewLocaltionX(itemLayout);
        final int y = getViewLocaltionY(itemLayout) - getStatusBarHeight();
        float indexY = y + (dialogMode ? -BaseUtils.getStatusBarHeight() : 0);
        float itemMeasureWidth = itemLayout.getMeasuredWidth();
        float itemMeasureHeight = itemLayout.getMeasuredHeight();
        Paint paint=new Paint();
        /**
         * Sawtooth removal
         */
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.FILL);
        /**
         * Set the color of paint
         */
        paint.setColor(Color.parseColor(color));
        Path path = new Path();

        path.moveTo(0, 0);
        path.lineTo(getScreenWidth(), 0);
        path.lineTo(getScreenWidth(), indexY);
        path.lineTo(x, indexY);
        path.lineTo(x, indexY + itemMeasureHeight);
        path.lineTo(x + itemMeasureWidth, indexY + itemMeasureHeight);
        path.lineTo(x + itemMeasureWidth, indexY);
        path.lineTo(getScreenWidth(), indexY);
        path.lineTo(getScreenWidth(), getScreenHeight());
        path.lineTo(0, getScreenHeight());
        path.close();
        canvas.drawPath(path, paint);
        super.draw(canvas);
    }

    private int getViewLocaltionX(View v){
        return getViewLocation(v)[0];
    }

    private int getViewLocaltionY(View v){
        return getViewLocation(v)[1];
    }

    private int[] getViewLocation(View v){
        int[] location = new int[2];
        v.getLocationOnScreen(location);
        return location;
    }

Note that indexY distinguishes between Dialog mode and Windows Manager mode, and the height of status_bar needs to be subtracted if the status bar exists.
These are the basic principles and ideas of high imitation Pinterest interaction.

Simplified Pinterest Interaction

Write at the end: Pinterest can't be opened Amway's Speedometer Agents, to enable you to Kaisu to open steadily Pinterest:

Accelerator Recommendation Free Program Payment scheme Official website
A Red Apricot Accelerator Free program is not available yet, stable and high-speed Enter 8% discount code wh80 and pay only 80 yuan per year. Official website direct accesshttp://whosmall.com/go/yzhx
Anyun Accelerator Best Use of Foreign Trade VPN Minimum 30/month Official website direct accesshttp://whosmall.com/go/ay
LoCo accelerator Free 2 hours a day Minimum 15/month Official website direct accesshttp://whosmall.com/go/loco

Label of this article: Pinterest can't be opened High imitation Pinterest Jianwoo

Turn from SUN'S BLOG - Focus on Internet knowledge and share the spirit of the Internet!

Original address:< What if Pinterest can't open? Interactive Implementation of High Imitation Pinterest>

Relevant reading:< Initial Experience of iOS 11's LivePhoto Long Exposure Function: Photographing Silky Flow>

Relevant reading:< How to install the latest version of LEMP / LNMP using Dotdeb?>

Relevant reading:< Ubuntu Server 16.04 Installation of LEMP/LNMP Tutorial>

Relevant reading:< Easy-to-use and full-featured Motion Mapping Tool on Mac System: GIF Brewery 3>

Relevant reading:< A Fast and Efficient File Duplication Tool on MacOS System: Gemini 2>

Relevant reading:< An Efficient Task Window Management Tool on MacOS System: HazeOver>

Relevant reading:< How does MacOS upload files to Google Drive with LaunchBar?>

Relevant reading:< Best Mac App Quick Start and Switch Tool: Manico 2.0>

Relevant reading:< Why did I choose Windows Tidy as a MacOS splitting tool?>

Relevant reading: Useful for programmers: the latest Google hosts file download in 2017 and summary of various hosts problems encountered by netizens and configuration details

Relevant BLOG: SUN'S BLOG - Focus on Internet knowledge and share Internet spirit! Go and see: www.whosmall.com

Original address: http://whosmall.com/?post=493

Keywords: Windows Fragment Android Mac

Added by brax23 on Sun, 23 Jun 2019 22:57:24 +0300