Android achieves QQ spatial picture drop-down effect (thunderstorm)

Keywords: Android xml encoding Attribute

When we open the QQ control and slide down the picture of his head, you will find that it has a larger effect. This effect is not difficult to achieve. Let's achieve it together.

First of all, we will analyze it, because QQ space, in addition to the picture below there are a lot of news released by our friends, even more than the news sent by our friends, and one by one, including different layouts, some pictures, some are plain text, some are both text and pictures and so on, in fact, these are simple ListView through the Header. The Header layout with pictures is added to the section. Each subitem is implemented with different layouts. I remember that I developed similar applications in the early days. So, today we can also customize ListView to achieve a larger drop-down effect on the top picture of QQ space.

Well, we have found the controls, so let's analyze the functions that need to be implemented. Step by step, the first thing is the pull-down effect. Come on, start coding. We inherit List View to customize the controls and implement the internal construction method. At the same time, there is an overScrollBy (int deltaX, int deltaY, int scrollX, int scrollY, int RangeX, int s. Croll RangeY, int Max Over ScrollX, int Max Over ScrollY, Boolean is TouchEvent, this method will be called when the View slides out of range. Here we use its second parameter, because it is up and down sliding, so we only care about the Y axis. When the user slides down ListView, shows the top and continues to slide down, the current method will be called. DeltaY is negative, when the user slides up and down. When moving ListView, the current method is also invoked when the bottom is exposed and continues to slide upward, with deltaY being a positive number. We can do something in this way. First of all, do the preparatory work, a header layout of ListView:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/img_header"
        android:layout_width="match_parent"
        android:layout_height="@dimen/header_drawable_height"
        android:scaleType="centerCrop"
        android:src="@drawable/timg" />
</LinearLayout>

Notice here that the height of ImageView is 200 DP dead, and the image is defined by the src attribute, scaleType is center Crop. Each subitem layout in ListView is as follows:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <TextView
        android:id="@+id/tv"
        android:layout_width="match_parent"
        android:layout_height="40dp"
        android:gravity="center"
        android:text="Chinese?"
        android:textSize="22dp" />
</LinearLayout>

Nothing here, just a TextView control. MyListView custom control code:

public class MyListView extends ListView {
    private ImageView mHeaderImg;

    public MyListView(Context context) {
        super(context);
    }

    public MyListView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setHeaderViewImg(ImageView img) {
        this.mHeaderImg = img;
    }

    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
        if (deltaY < 0) {
            mHeaderImg.getLayoutParams().height = mHeaderImg.getHeight() - deltaY;
            mHeaderImg.requestLayout();
        }
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
    }
}

Here I inherit ListView to implement the related functions. I define an ImageView member variable of mHeaderImg internally. This variable is used to save the ImageView to be enlarged in the HeaderView passed in from outside, which is passed in through the setHeaderView Img method. Then I rewrite the overScrollBy() method, which is based on the judgement of deltay < 0. If you stretch beyond the range, you need to change the height of mHeaderImg and re-measure the drawing. Take another look at the code in MainActivity:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyListView myListView = (MyListView) findViewById(R.id.myListView);
        ArrayAdapter adapter = new ArrayAdapter(this, R.layout.item_listview, R.id.tv, new String[]{
                "Chinese?", "American", "Sweden", "Russians", "Japanese", "A German", "Italian", "French", "Britisher", "Chileans",
                "Russians", "Japanese", "A German", "Italian", "French", "Britisher", "Chileans"
        });
        View headerView = getWindow().getLayoutInflater().inflate(R.layout.header, null);
        ImageView img = (ImageView) headerView.findViewById(R.id.img_header);
        myListView.setHeaderViewImg(img);
        myListView.addHeaderView(headerView);
        myListView.setAdapter(adapter);
    }

}

Well, the preparatory work is done and the effect of implementation is examined.


Well, yes, it's sliding down and the picture is enlarged, but you'll find that when we slide down and the picture gets bigger and slides up, the picture doesn't recover, but slides up directly. So how can we make him recover? This can't be dealt with in overScrollViewBy (), because it doesn't exceed the sliding range, so we won't execute the method. Here, we implement it. The onScrollChanged() method is as follows:

@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
    View vParent= (View) mHeaderImg.getParent();
    if(mHeaderImg.getHeight()>mDrawAbleHeight&&vParent.getTop()<0){
        mHeaderImg.getLayoutParams().height = mHeaderImg.getHeight() + vParent.getTop();
        vParent.layout(0,0,mHeaderImg.getWidth(),mHeaderImg.getHeight());
        mHeaderImg.requestLayout();
    }
    super.onScrollChanged(l, t, oldl, oldt);
}

Firstly, the outer layout of the enlarged image is acquired to determine whether the current image height is higher than the original image height, and the getTop() value of the outer layout is less than 0 (indicating that the screen is sliding upward). If the conditions are satisfied, the height of the picture is changed. The height of the image to be added here is the distance from the outer layout to the top, because it is a negative value, so add. Then because our image height has changed, the parent layout has been re-measured and the image has to be re-measured. Take a look at the results of the operation:


Has it solved the problem that we can't recover? Perfect, are you little buddies? If you feel yes, that's wrong. In fact, there are still bug s in our project. Let's delete a part of the data and see:


You'll find that when we have fewer entries, the drop-down enlarges, but when we don't go beyond the screen below, we slide up again and the picture won't recover. Because the entire ListView can be fully displayed, we won't perform sliding, and we won't execute onScrollChanged() method, so here we are still doing things in the overScollBy() method. Let's add an else judgment. Yes, as follows:

@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
    if (deltaY < 0) {
        mHeaderImg.getLayoutParams().height = mHeaderImg.getHeight() - deltaY;
        mHeaderImg.requestLayout();
    }else if (deltaY >= 0 && mHeaderImg.getHeight() > mDrawAbleHeight) {
        mHeaderImg.getLayoutParams().height = mHeaderImg.getHeight() - deltaY;
        //Although the height has changed, it must be called to re-measure.
        mHeaderImg.requestLayout();
    }
    return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent);
}

Take another look at the effect:


Once again, the problem was solved. Another problem is that QQ controls have a bouncing effect when we pull and loosen them. Let's add that we need to listen for the lifting action, so here we need to rewrite onTouchEvent (Motion Event ev) to see the code implementation:

@Override
public boolean onTouchEvent(MotionEvent ev) {
    switch (ev.getAction()) {
        case MotionEvent.ACTION_UP:
            if (mHeaderImg.getHeight() > mDrawAbleHeight) {
                MyAnimation myAnimation = new MyAnimation(mHeaderImg, mDrawAbleHeight);
                myAnimation.setDuration(200);
                mHeaderImg.startAnimation(myAnimation);
            }
            break;
    }

    return super.onTouchEvent(ev);
}

It's also very simple. When we raise our finger, we can judge whether the current image height is higher than the original image height. If it is higher than that, I define an Animation here. Setting the execution time to 200 milliseconds, we can start Animation. When we initialize the Animation, we can see that when we pass in the current image, we can see the implementation of the custom Animation with the original size of the image.

public class MyAnimation extends Animation {
    private int nowImgHeight;
    private int imgHeightDiffer;

    public MyAnimation(ImageView img, int initedImgHeight) {
        nowImgHeight = img.getHeight();
        imgHeightDiffer = nowImgHeight - initedImgHeight;

    }

    @Override
    protected void applyTransformation(float interpolatedTime, Transformation t) {
        mHeaderImg.getLayoutParams().height = (int) (nowImgHeight - imgHeightDiffer * interpolatedTime);
        //Although the height has changed, it must be called to re-measure.
        mHeaderImg.requestLayout();
        super.applyTransformation(interpolatedTime, t);
    }
}

The main thing is to rewrite the applyTransFormation () method. We only use the first two parameters here. He expresses the current animation execution level, a change process from 0 to 1. Understanding these, we can change the image height here. First, we find the distance we need to move in the prescribed 200 milliseconds, that is, the current height minus the original height is that we are in 2 milliseconds. The distance that needs to be moved within 00 milliseconds, so that the current distance minus the execution level of the current time is equal to the current height of the picture, and then redraw the current picture. Well, everything's all right. Let's execute it and see the effect.


At this point, the whole function is completed, in fact, there are many operations can be done, such as enlarging to a certain extent can not be enlarged, adding a small picture in header, executing animation, achieving similar effects of micro-letters pull-down rotation, and so on. Everyone can try to achieve it, click on the source code now. Okay, that's all for today, tragedy is still working overtime, today's version is online, my bugs have been completely solved, but I can't go home, only wait for other colleagues to solve the bugs on the line before leaving, take some time to sort out small things to share a good, not to say that the line is over, how is it still discussed, what is the situation, brothers, in the discussion on two points, sad. Drama!









Posted by argh2xxx on Thu, 20 Jun 2019 12:32:04 -0700