Chapter 4 ListView Use Skills - The rest

Posted by adamlacombe on Sat, 29 Jun 2019 23:21:53 +0200

4.1.8 Processing Empty ListView

ListView is used to display list data, but when there is no data in the list, ListView will not display any data or prompts. According to the need to improve the user experience, there should be no data prompts here. Fortunately, ListView provides a method, setEmptyView(), through which we can set a default prompt for ListView to display under empty data. The layout settings containing ListView are as follows:

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

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none" />

    <ImageView
        android:id="@+id/img_empty"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:src="@mipmap/ic_launcher" />

</FrameLayout>

In the code, we set the layout of empty data for ListView in the following way. The code is as follows:

listView = (ListView) findViewById(R.id.list_view);
imgEmpty = (ImageView) findViewById(R.id.img_empty);
listView.setEmptyView(imgEmpty);

With the above code, ListView is given a default image to prompt the user when empty data is available, but not when data is available. The code for MainActivity is as follows:

package com.example.test;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.ImageView;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity {

    private ListView listView;
    private List<String> mData = new ArrayList<>();
    private ViewHolderAdapter adapter;
    private ImageView imgEmpty;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.list_view);
        imgEmpty = (ImageView) findViewById(R.id.img_empty);
//The following code is commented out to indicate that there is no data
//        for (int i = 0; i < 30; i++) {
//            mData.add(i + "");
//        }
        adapter = new ViewHolderAdapter(this, mData);
        listView.setAdapter(adapter);
        listView.setEmptyView(imgEmpty);
    }

}

4.1.9 ListView Slide Monitor

Sliding monitoring of ListView is the most important skill in ListView. Many rewritten ListViews basically focus on the processing of sliding events, and make different logical processing by judging the sliding events. In order to monitor sliding events more accurately, developers usually need to use Gesture Detector gesture recognition, Velocity Tracker sliding speed detection and other auxiliary classes to complete better monitoring. Here we introduce two ways to monitor ListView sliding events. One is through OnTouchListener, the other is using OnScrollListener.

(1)OnTouchListener

OnTouchListener is a listening event in View. By monitoring the coordinates of ACTION_DOWN, ACTION_MOVE and ACTION_UP when these three events occur, the user can judge the direction of sliding according to the coordinates, and carry out corresponding logical processing in different events. The use code of this way is as follows:

listView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                switch (event.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        //Operation while touching
                        break;
                    case MotionEvent.ACTION_MOVE:
                        //Mobile operation
                        break;
                    case MotionEvent.ACTION_UP:
                        //Operation on departure
                        break;
                }
                return false;
            }
        });

(2)OnScrollListener

OnScrollListener is a listening event in AbsListView. It encapsulates a lot of information related to ListView and is more flexible to use. First, let's look at the general usage of OnScrollListener. The code is as follows:

listView.setOnScrollListener(new AbsListView.OnScrollListener() {
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        switch (scrollState) {
            case SCROLL_STATE_IDLE:
                //When sliding stops

                Log.i(TAG, "SCROLL_STATE_IDLE");
                break;
            case SCROLL_STATE_TOUCH_SCROLL:
                //Rolling

                Log.i(TAG, "SCROLL_STATE_TOUCH_SCROLL");
                break;
            case SCROLL_STATE_FLING:
                //When the finger is thrown, that is, the finger is forced to slide. After leaving, ListView continues to slide due to inertia.

                Log.i(TAG, "SCROLL_STATE_FLING");
                break;
        }
    }

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        //Always call when scrolling

        Log.i(TAG, "onScroll");
    }
});

OnScrollStateChanged() and onScroll() are two callback methods in OnScrollListener.
Let's first look at the first method, OnScrollStateChanged(), which determines the number of callbacks based on its parameter scrollState. There are three modes of scrollState:

  • SCROLL_STATE_IDEL: When the scroll stops.
  • SCROLL_STATE_TOUCH_SCROLL: While scrolling.
  • SCROLL_STATE_FLING: When a finger is thrown, the finger is forced to slide, and after leaving, ListView continues to slide due to inertia.

    When the user does not do the state of finger throwing, this method will only call back twice, otherwise it will call back three times, the difference is the state of finger throwing. Usually, in this method, we will set some flags Flag by different states to distinguish different sliding states for other methods to deal with.
    Let's take a look at OnScroll() as a callback method, which always calls back when the ListView scrolls. The last three int parameters in the method show the current status of the ListView scroll very accurately. These three parameters are as follows:

    • First Visible Item: The ID of the first Item currently visible (starting from 0).
    • Visible ItemCount: The total number of Items currently visible.
    • TotItemCount: The total number of Items for the entire ListView.

      It should be noted here that the number of Items currently visible, including those that do not display the complete Item, i.e. a half of the Item, is also included. Through these parameters, it is easy to make some judgments, such as whether to scroll to the last line, you can use the following code to make judgments. When the ID of the current visual first Item plus the sum of the current visual Item equals the total number of Items, that is, scroll to the last line.

if (firstVisibleItem + visivleItemCount == totalItemCount & totalItemCount >0){
    //Scroll to the last line
}

For example, the direction of the scroll can be determined by the following code, which is as follows:

if (firstVisibleItem > lastVisibleItemPosition){
    //Up slide
}else if (firstVisibleItem < lastVisibleItemPosition){
    //Slide downward
}
lastVisibleItemPosition = firstVisibleItem;

A member variable lastVisibleItemPosition is used to record the ID of the last visible Item and compare it with the ID of the current visible Item to know the direction of the current scroll.
To understand the whole OnScrollListener, the best way is to add Log to the code and print out the status information for analysis and learning. In the above code, the corresponding Log has been added, compared with Log analysis, will quickly grasp the use of OnScrollListener.
Of course, ListView also provides us with some encapsulation methods to get information about the location of the current visual Item, etc.

//Get the ID of the last Item in the visual area
listView.getLastVisiblePosition();
//Get the ID of the first Item in the visual area
listView.getFirstVisiblePosition();

4.2 Common Extensions of ListView

Although ListView is widely used, the original ListView of the system obviously can not meet the users'increasing aesthetic and functional needs. Nevertheless, it doesn't matter. Android can be completely customized, which makes it very convenient for us to expand and modify the native ListView. As a result, with the innovations of developers, ListView is becoming more and more colorful, and various extensions based on native ListView are dazzling. Here are some common ListView extensions.

4.2.1 ListView with Elasticity

Android's default ListView does not give a good hint when scrolling to the top or bottom. In Android 5.X, Google added only a half-moon shadow effect to such behavior. In IOS systems, lists are resilient, that is, they scroll to the bottom or the top and continue to slide down or up for a certain distance. It has to be said that such a design is indeed more friendly, although I don't know why Google does not imitate such a design, but we can modify ListView ourselves, so that ListView can also be "flexible".
Here we can use a very simple way to achieve this effect. When we look at the ListView source code, we can see that there is a way to control sliding to the edge in ListView, as follows:

protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX 
          int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)

You can see this parameter: maxOverScrollY, which is written in the Commentary --- Number of pixels to overscroll by in either direction along the Y axis. It can be found that, although its default value is 0, in fact, as long as the value of this parameter is modified, ListView can be flexible! Rewrite this method and change maxOverScrollY to the set value, mMaxOverDistance, as follows:

@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY,
                              int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
    return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX,
                              scrollRangeY, maxOverScrollX, mMaxOverDistance, isTouchEvent);
}

Thus, by modifying this value, an elastic ListView is implemented. Of course, in order to meet the needs of multi-resolution, we can modify the value of maxOverScrollY by calculating the specific value through the density of the screen, so that the elastic distances of different resolutions are basically the same. The code is as follows:

private void initView() {
    DisplayMetrics dm = this.getResources().getDisplayMetrics();
    mMaxOverDistance = (int) (dm.density * mMaxOverDistance);
}

Finally, all the code is sorted out as follows:
Custom ListView:

package com.example.test;

import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.widget.ListView;

/**
* Custom ListView
* Created by HourGlassRemember on 2016/9/28.
*/
public class CustomListView extends ListView {

    private int mMaxOverDistance = 60;

    public CustomListView(Context context) {
        this(context, null);
    }

    public CustomListView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CustomListView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initView();
    }

    /**
    * Initialization

    */
    private void initView() {
        DisplayMetrics dm = this.getResources().getDisplayMetrics();
        mMaxOverDistance = (int) (dm.density * mMaxOverDistance);
    }

    @Override
    protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, 
                      int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
        return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
                                  maxOverScrollX, mMaxOverDistance, isTouchEvent);
    }

}

Layout file:

<?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="match_parent">

    <com.example.test.CustomListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none" />

</LinearLayout>

Adapter:

package com.example.test;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

/**
* Created by HourGlassRemember on 2016/9/18.
*/
public class ViewHolderAdapter extends BaseAdapter {

    private List<String> mData;
    private LayoutInflater mInflater;

    public ViewHolderAdapter(Context context, List<String> mData) {
        this.mData = mData;
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public Object getItem(int position) {
        return mData.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder = null;
        //Determine whether to cache or not

        if (convertView == null) {
            holder = new ViewHolder();
            //Instantiate layout through LayoutInflate

            convertView = mInflater.inflate(R.layout.viewholder_item, null);
            holder.imgIcon = (ImageView) convertView.findViewById(R.id.img_icon);
            holder.txtTitle = (TextView) convertView.findViewById(R.id.txt_title);
            convertView.setTag(holder);
        } else {
            //Find the layout of the cache by tag

            holder = (ViewHolder) convertView.getTag();
        }
        //Set the view to be displayed by the control in the layout

        holder.imgIcon.setBackgroundResource(R.mipmap.ic_launcher);
        holder.txtTitle.setText(mData.get(position));
        return convertView;
    }

    public final class ViewHolder {
        public ImageView imgIcon;
        public TextView txtTitle;
    }

}

MainActivity class:

package com.example.test;

import android.app.Activity;
import android.os.Bundle;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";

    private CustomListView listView;
    private List<String> mData = new ArrayList<>();
    private ViewHolderAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (CustomListView) findViewById(R.id.list_view);
        for (int i = 0; i < 30; i++) {
            mData.add(i + "");
        }
        adapter = new ViewHolderAdapter(this, mData);
        listView.setAdapter(adapter);
    }

}

4.2.2 ListView that automatically displays and hides layout

I believe that friends who have used Google + should be very familiar with the effect that when we slide on ListView, the top ActionBar or Toolbar will be hidden or displayed accordingly. You can find that the title bar above and the suspension editing button in the lower right corner are loaded on the scroll front interface. When the user scrolls down, the title bar and the suspension button disappear, giving the user more space to read. Let's follow this example to design a similar effect. The code is as follows:
Layout file:

<?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="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#FF0000" />

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none" />

</LinearLayout>

MainActivity class:

package com.example.test;

import android.animation.ObjectAnimator;
import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.Toolbar;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AbsListView;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";

    private ListView listView;
    private List<String> mData = new ArrayList<>();
    private ViewHolderAdapter adapter;
    //The minimum sliding distance considered by the system

    private float mTouchSlop;
    private float mFirstY, mCurrentY;
    //Describe the sliding direction of the finger. Direction 1 represents upward and direction 0 represents downward.

    private int direction;
    private boolean mShow = true;
    private Toolbar mToolbar;
    private ObjectAnimator mAnimator;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        listView = (ListView) findViewById(R.id.list_view);

        //Add HeaderView to ListView to avoid the first Item being blocked by Toolbar

        View headerView = new View(this);
        headerView.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.MATCH_PARENT,
                (int) getResources().getDimension(R.dimen.abc_action_bar_default_height_material)));
        listView.addHeaderView(headerView);

        for (int i = 0; i < 30; i++) {
            mData.add(i + "");
        }
        adapter = new ViewHolderAdapter(this, mData);
        listView.setAdapter(adapter);

        //Obtain the minimum sliding distance considered by the system

        mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
        listView.setOnTouchListener(mTouchListener);
    }

    View.OnTouchListener mTouchListener = new View.OnTouchListener() {
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    mFirstY = event.getY();
                    break;
                case MotionEvent.ACTION_MOVE:
                    mCurrentY = event.getY();
                    //Calculate whether the finger is sliding up or down

                    direction = mCurrentY - mFirstY > mTouchSlop ? 0 : 1;
                    if (direction == 1 && mShow) {//wipe up

                        toolbarAnim(1);//Hide HeaderView
                    } else if (direction == 0 && !mShow) {//Slide down

                        toolbarAnim(0);//Display HeaderView
                    }
                    mShow = !mShow;
                    break;
                case MotionEvent.ACTION_UP:
                    break;
            }
            return false;
        }
    };

    /**
    * Control layout to display hidden animations

    *
    * @param flag
    */
    private void toolbarAnim(int flag) {
        //Cancel the previous animation before starting a new animation

        if (mAnimator != null && mAnimator.isRunning()) {
            mAnimator.cancel();
        }
        mAnimator = ObjectAnimator.ofFloat(mToolbar, "translationY",
              mToolbar.getTranslationY(), flag == 0 ? 0 : -mToolbar.getHeight());
        mAnimator.start();
    }

}

4.2.3 Chat ListView

Usually, each of the ListViews we use has the same layout, so the layout should be similar except for the different data, as long as you don't hide the layout. We are familiar with QQ, Wechat and other chat App s, in the chat interface, will show at least two layouts, namely, received messages and messages sent by ourselves. In fact, this effect is also achieved through ListView. Here we will imitate a chat list interface of chat software. The effect is as follows:

The biggest difference between such a ListView and the ListView that we usually use is that it has two different layouts -- the layout received and the layout sent. To achieve this effect, you need to use ListView's Adapter to "open the knife". The specific code is as follows:

The layout of sender Item (chat_item_send.xml):

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

    <ImageView
        android:id="@+id/img_send"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_alignParentRight="true"
        android:layout_centerVertical="true"
        android:scaleType="centerCrop"
        android:src="@drawable/img2" />

    <TextView
        android:id="@+id/txt_send"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginRight="10dp"
        android:layout_toLeftOf="@+id/img_send"
        android:background="@drawable/chat_send"
        android:gravity="center"
        android:text="Sender" />

</RelativeLayout>

The layout of the receiver Item (chat_item_receive.xml):

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

    <ImageView
        android:id="@+id/img_receive"
        android:layout_width="50dp"
        android:layout_height="50dp"
        android:layout_centerVertical="true"
        android:scaleType="centerCrop"
        android:src="@drawable/img1" />

    <TextView
        android:id="@+id/txt_receive"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerVertical="true"
        android:layout_marginLeft="10dp"
        android:layout_toRightOf="@+id/img_receive"
        android:background="@drawable/chat_receive"
        android:gravity="center"
        android:text="Receiver" />

</RelativeLayout>

ViewHolder Adapter for ListView:

package com.example.test;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.List;

/**
* Created by HourGlassRemember on 2016/9/18.
*/
public class ViewHolderAdapter extends BaseAdapter {

    private List<ChatItemBean> mData;
    private LayoutInflater mInflater;

    public ViewHolderAdapter(Context context, List<ChatItemBean> mData) {
        this.mData = mData;
        mInflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public Object getItem(int position) {
        return mData.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    /**
    * What type of Item to get the position

    *
    * @param position
    * @return
    */
    @Override
    public int getItemViewType(int position) {
        return mData.get(position).getType();
    }

    /**
    * Returns the total number of different types of layouts

    *
    * @return
    */
    @Override
    public int getViewTypeCount() {
        return 2;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder holder;
        //Determine whether to cache or not

        if (convertView == null) {
            //Determine which layout is based on the type of getItemViewType

            if (getItemViewType(position) == 0) {//Receiver -- "the other party"
                holder = new ViewHolder();
                convertView = mInflater.inflate(R.layout.chat_item_receive, parent, false);
                holder.imgHead = (ImageView) convertView.findViewById(R.id.img_receive);
                holder.txtContent = (TextView) convertView.findViewById(R.id.txt_receive);
            } else {//Sender - "I"
                holder = new ViewHolder();
                convertView = mInflater.inflate(R.layout.chat_item_send, parent, false);
                holder.imgHead = (ImageView) convertView.findViewById(R.id.img_send);
                holder.txtContent = (TextView) convertView.findViewById(R.id.txt_send);
            }
            convertView.setTag(holder);
        } else {
            //Find the layout of the cache by tag

            holder = (ViewHolder) convertView.getTag();
        }
        if (mData != null && mData.size() > 0) {
            holder.imgHead.setImageBitmap(mData.get(position).getHead());
            holder.txtContent.setText(mData.get(position).getContent());
        }
        return convertView;
    }

    public final class ViewHolder {
        public ImageView imgHead;
        public TextView txtContent;
    }

}

MainActivity layout file:

<?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="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none" />

</LinearLayout>

MainActivity class:

package com.example.test;

import android.app.Activity;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";

    private ListView listView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.list_view);

        List<ChatItemBean> data = new ArrayList<>();
        data.add(addChatContent(1, "Hi,You are?!"));
        data.add(addChatContent(0, "Hello!"));
        data.add(addChatContent(1, "Hello"));
        data.add(addChatContent(0, "Are you there?"));
        data.add(addChatContent(1, "Where are you from?"));
        data.add(addChatContent(0, "Have a guess"));
        listView.setAdapter(new ViewHolderAdapter(this, data));
        listView.setDivider(null);
    }

    /**
    * Add chat content

    *
    * @param type
    * @param content
    */
    private ChatItemBean addChatContent(int type, String content) {
        ChatItemBean chatItemBean = new ChatItemBean();
        chatItemBean.setType(type);
        chatItemBean.setHead(BitmapFactory.decodeResource(getResources(),
                             type == 0 ? R.drawable.img1 : R.drawable.img2));
        chatItemBean.setContent(content);
        return chatItemBean;
    }

}

4.2.4 Dynamic Change ListView Layout

Usually, if you want to dynamically change the layout of clicking on Item to hit a Focus effect, there are two ways: one is to write the two layouts together, by controlling the display and hiding of the layout, to achieve the effect of switching the layout; the other is to select the addition by judgment when getView(). Load different layouts. Both methods have their own advantages and disadvantages, the key is to use the occasion. Here is a second way to demonstrate this effect. After the program runs, the initial effect is as follows. The first Item defaults to Focus state. When clicking on other Items, the Item clicked becomes Focus and the other Items are restored.


(Initial state of program)


(Focus change)

The code for the implementation is as follows:
Layout file:

<?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="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none" />

</LinearLayout>

ViewHolder Adapter for ListView:

package com.example.test;

import android.content.Context;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;

import java.util.List;

/**
* Created by HourGlassRemember on 2016/9/18.
*/
public class ViewHolderAdapter extends BaseAdapter {

    private List<String> mData;
    private Context mContext;
    //Location of the current Item

    private int mCurrentItem;

    public ViewHolderAdapter(Context mContext, List<String> mData) {
        this.mContext = mContext;
        this.mData = mData;
    }

    public void setCurrentItem(int mCurrentItem) {
        this.mCurrentItem = mCurrentItem;
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public Object getItem(int position) {
        return mData.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    /**
    * Add the layout of selected Item

    *
    * @param i
    * @return
    */
    private View addFocusView(int i) {
        LinearLayout layout = new LinearLayout(mContext);
        ImageView imageView = new ImageView(mContext);
        imageView.setImageResource(R.drawable.img1);
        layout.addView(imageView, new LinearLayout.LayoutParams(200, 200));
        layout.setGravity(Gravity.CENTER);
        return layout;
    }

    /**
    * Add normal Item layout

    *
    * @param i
    * @return
    */
    private View addNormalView(int i) {
        LinearLayout layout = new LinearLayout(mContext);
        layout.setOrientation(LinearLayout.HORIZONTAL);
        ImageView imageView = new ImageView(mContext);
        imageView.setImageResource(R.drawable.img3);
        layout.addView(imageView, new LinearLayout.LayoutParams(200, 200));
        TextView textView = new TextView(mContext);
        textView.setText(mData.get(i));
        layout.addView(textView, new LinearLayout.LayoutParams(200, 200));
        return layout;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        LinearLayout layout = new LinearLayout(mContext);
        layout.setOrientation(LinearLayout.VERTICAL);
        //By judging whether the current current Item is the position of that click, you can dynamically control the layout of the display.

        layout.addView(mCurrentItem == position ? addFocusView(position) : addNormalView(position));
        return layout;
    }

}

MainActivity class:

package com.example.test;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends Activity {

    private static final String TAG = "MainActivity";

    private ListView listView;
    private ViewHolderAdapter adapter;
    private List<String> mData = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView = (ListView) findViewById(R.id.list_view);

        for (int i = 0; i < 20; i++) {
            mData.add(i + "");
        }
        adapter = new ViewHolderAdapter(this, mData);
        listView.setAdapter(adapter);
        //Rewrite the monitoring event of Item click in ListView and record the location of Item click.
        //And let ListView refresh once to update the content of the interface

        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                adapter.setCurrentItem(position);
                adapter.notifyDataSetChanged();
            }
        });
    }

}

Topics: Android Java xml encoding