高级控件——列表类视图——列表视图ListView
====================================================================================
首页布局;
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="5dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="40dp"> <CheckBox android:id="@+id/ck_divider" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="left|center" android:text="显示分隔线" android:textColor="@color/black" android:textSize="17sp" /> <CheckBox android:id="@+id/ck_selector" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="left|center" android:text="显示按压背景" android:textColor="@color/black" android:textSize="17sp" /> </LinearLayout> <ListView android:id="@+id/lv_planet" android:layout_width="match_parent" android:layout_height="wrap_content" /> <!-- <ListView--> <!-- android:id="@+id/lv_planet"--> <!-- android:layout_width="match_parent"--> <!-- android:layout_height="0dp"--> <!-- android:layout_weight="1"--> <!-- android:divider="@null"--> <!-- android:dividerHeight="0dp"--> <!-- android:listSelector="@null"--> <!-- android:listSelector="@color/transparent" />--> </LinearLayout>
代码:
package com.example.myapplication; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.ListView; import androidx.appcompat.app.AppCompatActivity; import java.util.List; public class MainActivity extends AppCompatActivity implements CompoundButton.OnCheckedChangeListener { private final static String TAG = "ListViewActivity"; private CheckBox ck_divider; private CheckBox ck_selector; private ListView lv_planet; // 声明一个列表视图对象 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ck_divider = findViewById(R.id.ck_divider); ck_selector = findViewById(R.id.ck_selector); ck_divider.setOnCheckedChangeListener(this); ck_selector.setOnCheckedChangeListener(this); List<Planet> planetList = Planet.getDefaultList(); // 获得默认的行星列表 // 构建一个行星列表的列表适配器 PlanetListAdapter adapter = new PlanetListAdapter(this, planetList); // 从布局视图中获取名叫lv_planet的列表视图 lv_planet = findViewById(R.id.lv_planet); lv_planet.setAdapter(adapter); // 设置列表视图的适配器 lv_planet.setOnItemClickListener(adapter); // 设置列表视图的点击监听器 lv_planet.setOnItemLongClickListener(adapter); // 设置列表视图的长按监听器 } @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { refreshListView(); // 刷新列表视图 } // 刷新列表视图 private void refreshListView() { // lv_planet.setCacheColorHint(Color.TRANSPARENT); // 防止滚动时列表拉黑 if (ck_divider.isChecked()) // 显示分隔线 { // 从资源文件获得图形对象 Drawable drawable = getResources().getDrawable(R.color.red); lv_planet.setDivider(drawable); // 设置列表视图的分隔线 lv_planet.setDividerHeight(Utils.dip2px(this, 5)); // 设置列表视图的分隔线高度 } else // 不显示分隔线 { lv_planet.setDivider(null); // 设置列表视图的分隔线 lv_planet.setDividerHeight(0); // 设置列表视图的分隔线高度 } if (ck_selector.isChecked()) // 显示按压背景 { lv_planet.setSelector(R.drawable.list_selector); // 设置列表项的按压状态图形 } else // 不显示按压背景 { //lv_planet.setSelector(null); // 直接设置null会报错,因为运行时报空指针异常 // 从资源文件获得图形对象 Drawable drawable = getResources().getDrawable(R.color.transparent); lv_planet.setSelector(drawable); // 设置列表项的按压状态图形 } } }
item_list布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <!-- 这是显示行星图片的图像视图 --> <ImageView android:id="@+id/iv_icon" android:layout_width="0dp" android:layout_height="80dp" android:layout_weight="1" android:scaleType="fitCenter" /> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" android:layout_marginLeft="5dp" android:orientation="vertical"> <!-- 这是显示行星名称的文本视图 --> <TextView android:id="@+id/tv_name" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="left|center" android:textColor="@color/black" android:textSize="20sp" /> <!-- 这是显示行星描述的文本视图 --> <TextView android:id="@+id/tv_desc" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="2" android:gravity="left|center" android:textColor="@color/black" android:textSize="13sp" /> </LinearLayout> </LinearLayout>
Planet
package com.example.myapplication; import java.util.ArrayList; import java.util.List; public class Planet { public int image; // 行星图标 public String name; // 行星名称 public String desc; // 行星描述 public Planet(int image, String name, String desc) { this.image = image; this.name = name; this.desc = desc; } private static int[] iconArray = {R.drawable.shuixing, R.drawable.jinxing, R.drawable.diqiu, R.drawable.huoxing, R.drawable.muxing, R.drawable.tuxing}; private static String[] nameArray = {"水星", "金星", "地球", "火星", "木星", "土星"}; private static String[] descArray = { "水星是太阳系八大行星最内侧也是最小的一颗行星,也是离太阳最近的行星", "金星是太阳系八大行星之一,排行第二,距离太阳0.725天文单位", "地球是太阳系八大行星之一,排行第三,也是太阳系中直径、质量和密度最大的类地行星,距离太阳1.5亿公里", "火星是太阳系八大行星之一,排行第四,属于类地行星,直径约为地球的53%", "木星是太阳系八大行星中体积最大、自转最快的行星,排行第五。它的质量为太阳的千分之一,但为太阳系中其它七大行星质量总和的2.5倍", "土星为太阳系八大行星之一,排行第六,体积仅次于木星" }; public static List<Planet> getDefaultList() { List<Planet> planetList = new ArrayList<Planet>(); for (int i = 0; i < iconArray.length; i++) { planetList.add(new Planet(iconArray[i], nameArray[i], descArray[i])); } return planetList; } }
Utils
package com.example.myapplication; import android.content.Context; public class Utils { // 根据手机的分辨率从 dp 的单位 转成为 px(像素) public static int dip2px(Context context, float dpValue) { // 获取当前手机的像素密度(1个dp对应几个px) float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); // 四舍五入取整 } // 根据手机的分辨率从 px(像素) 的单位 转成为 dp public static int px2dip(Context context, float pxValue) { // 获取当前手机的像素密度(1个dp对应几个px) float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); // 四舍五入取整 } // 根据手机的分辨率从 sp 的单位 转成为 px(像素) public static int sp2px(Context context, float spValue) { // 获取当前手机的像素密度(1个dp对应几个px) float scale = context.getResources().getDisplayMetrics().density; return (int) (spValue * scale + 0.5f); // 四舍五入取整 } // 根据手机的分辨率从 px(像素) 的单位 转成为 sp public static int px2sp(Context context, float pxValue) { // 获取当前手机的像素密度(1个dp对应几个px) float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); // 四舍五入取整 } }
PlanetListAdapter:
package com.example.myapplication; import android.annotation.SuppressLint; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemLongClickListener; import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import java.util.List; @SuppressLint("DefaultLocale") public class PlanetListAdapter extends BaseAdapter implements OnItemClickListener, OnItemLongClickListener { private Context mContext; // 声明一个上下文对象 private List<Planet> mPlanetList; // 声明一个行星信息列表 // 行星适配器的构造方法,传入上下文与行星列表 public PlanetListAdapter(Context context, List<Planet> planet_list) { mContext = context; mPlanetList = planet_list; } // 获取列表项的个数 public int getCount() { return mPlanetList.size(); } // 获取列表项的数据 public Object getItem(int arg0) { return mPlanetList.get(arg0); } // 获取列表项的编号 public long getItemId(int arg0) { return arg0; } // 获取指定位置的列表项视图 public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { // 转换视图为空 holder = new ViewHolder(); // 创建一个新的视图持有者 // 根据布局文件item_list.xml生成转换视图对象 convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list, null); holder.iv_icon = convertView.findViewById(R.id.iv_icon); holder.tv_name = convertView.findViewById(R.id.tv_name); holder.tv_desc = convertView.findViewById(R.id.tv_desc); convertView.setTag(holder); // 将视图持有者保存到转换视图当中 } else // 转换视图非空 { // 从转换视图中获取之前保存的视图持有者 holder = (ViewHolder) convertView.getTag(); } Planet planet = mPlanetList.get(position); holder.iv_icon.setImageResource(planet.image); // 显示行星的图片 holder.tv_name.setText(planet.name); // 显示行星的名称 holder.tv_desc.setText(planet.desc); // 显示行星的描述 holder.iv_icon.requestFocus(); return convertView; } // 定义一个视图持有者,以便重用列表项的视图资源 public final class ViewHolder { public ImageView iv_icon; // 声明行星图片的图像视图对象 public TextView tv_name; // 声明行星名称的文本视图对象 public TextView tv_desc; // 声明行星描述的文本视图对象 } // 处理列表项的点击事件,由接口OnItemClickListener触发 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String desc = String.format("您点击了第%d个行星,它的名字是%s", position + 1, mPlanetList.get(position).name); Toast.makeText(mContext, desc, Toast.LENGTH_LONG).show(); } // 处理列表项的长按事件,由接口OnItemLongClickListener触发 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { String desc = String.format("您长按了第%d个行星,它的名字是%s", position + 1, mPlanetList.get(position).name); Toast.makeText(mContext, desc, Toast.LENGTH_LONG).show(); return true; } }
============================================================================
===========================================================================================
===========================================================================================
布局:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="5dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="40dp"> <TextView android:layout_width="wrap_content" android:layout_height="match_parent" android:gravity="center" android:text="焦点抢占方式:" android:textColor="@color/black" android:textSize="17sp" /> <Spinner android:id="@+id/sp_focus" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:gravity="left|center" android:spinnerMode="dialog" /> </LinearLayout> <ListView android:id="@+id/lv_planet" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider="@null" android:dividerHeight="0dp" android:listSelector="@color/transparent" /> </LinearLayout>
代码:
package com.example.myapplication; import android.graphics.drawable.Drawable; import android.os.Bundle; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.CheckBox; import android.widget.CompoundButton; import android.widget.ListView; import android.widget.Spinner; import androidx.appcompat.app.AppCompatActivity; import java.util.List; public class MainActivity extends AppCompatActivity { private final static String TAG = "ListFocusActivity"; private ListView lv_planet; // 声明一个列表视图对象 private PlanetListWithButtonAdapter adapter; // 行星列表的列表适配器 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFocusSpinner(); // 初始化焦点抢占方式的下拉框 } // 初始化焦点抢占方式的下拉框 private void initFocusSpinner() { ArrayAdapter<String> focusAdapter = new ArrayAdapter<String>(this, R.layout.item_select, focusArray); Spinner sp_focus = findViewById(R.id.sp_focus); sp_focus.setPrompt("请选择焦点抢占方式"); // 设置下拉框的标题 sp_focus.setAdapter(focusAdapter); // 设置下拉框的数组适配器 sp_focus.setSelection(0); // 设置下拉框默认显示第一项 // 给下拉框设置选择监听器,一旦用户选中某一项,就触发监听器的onItemSelected方法 sp_focus.setOnItemSelectedListener(new FocusSelectedListener()); } private String[] focusArray = { "在子控件之前处理", "在子控件之后处理", "不让子控件处理", }; class FocusSelectedListener implements AdapterView.OnItemSelectedListener { public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) { if (arg2 == 0) { // 在子控件之前处理 showListView(ViewGroup.FOCUS_BEFORE_DESCENDANTS); // 显示指定抢占方式的列表视图 } else if (arg2 == 1) { // 在子控件之后处理 showListView(ViewGroup.FOCUS_AFTER_DESCENDANTS); // 显示指定抢占方式的列表视图 } else if (arg2 == 2) { // 不让子控件处理,此时才会响应列表项的点击和长按事件 showListView(ViewGroup.FOCUS_BLOCK_DESCENDANTS); // 显示指定抢占方式的列表视图 } } public void onNothingSelected(AdapterView<?> arg0) {} } // 显示指定抢占方式的列表视图 private void showListView(int focusMethod) { List<Planet> planetList = Planet.getDefaultList(); // 获得默认的行星列表 // 构建一个行星列表的列表适配器 adapter = new PlanetListWithButtonAdapter(this, planetList, focusMethod); // 从布局视图中获取名叫lv_planet的列表视图 lv_planet = findViewById(R.id.lv_planet); lv_planet.setAdapter(adapter); // 设置列表视图的适配器 lv_planet.setOnItemClickListener(adapter); // 设置列表视图的点击监听器 lv_planet.setOnItemLongClickListener(adapter); // 设置列表视图的长按监听器 } }
item_list_with_button:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_item" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <!-- blocksDescendants可阻止下级控件获得焦点,避免堵塞列表视图的点击事件 android:descendantFocusability="blocksDescendants" --> <!-- 这是显示行星图片的图像视图 --> <ImageView android:id="@+id/iv_icon" android:layout_width="0dp" android:layout_height="80dp" android:layout_weight="1" android:scaleType="fitCenter" /> <LinearLayout android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="3" android:layout_marginLeft="5dp" android:orientation="vertical"> <!-- 这是显示行星名称的文本视图 --> <TextView android:id="@+id/tv_name" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" android:gravity="left|center" android:textColor="@color/black" android:textSize="20sp" /> <!-- 这是显示行星描述的文本视图 --> <TextView android:id="@+id/tv_desc" android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="2" android:gravity="left|center" android:textColor="@color/black" android:textSize="13sp" /> </LinearLayout> <Button android:id="@+id/btn_oper" android:layout_width="50dp" android:layout_height="match_parent" android:gravity="center" android:text="点我" android:textColor="@color/black" android:textSize="17sp" /> </LinearLayout>
item_select:
<TextView xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/tv_name" android:layout_width="match_parent" android:layout_height="50dp" android:singleLine="true" android:gravity="center" android:textSize="17sp" android:textColor="#0000ff" />
Planet
package com.example.myapplication; import java.util.ArrayList; import java.util.List; public class Planet { public int image; // 行星图标 public String name; // 行星名称 public String desc; // 行星描述 public Planet(int image, String name, String desc) { this.image = image; this.name = name; this.desc = desc; } private static int[] iconArray = {R.drawable.shuixing, R.drawable.jinxing, R.drawable.diqiu, R.drawable.huoxing, R.drawable.muxing, R.drawable.tuxing}; private static String[] nameArray = {"水星", "金星", "地球", "火星", "木星", "土星"}; private static String[] descArray = { "水星是太阳系八大行星最内侧也是最小的一颗行星,也是离太阳最近的行星", "金星是太阳系八大行星之一,排行第二,距离太阳0.725天文单位", "地球是太阳系八大行星之一,排行第三,也是太阳系中直径、质量和密度最大的类地行星,距离太阳1.5亿公里", "火星是太阳系八大行星之一,排行第四,属于类地行星,直径约为地球的53%", "木星是太阳系八大行星中体积最大、自转最快的行星,排行第五。它的质量为太阳的千分之一,但为太阳系中其它七大行星质量总和的2.5倍", "土星为太阳系八大行星之一,排行第六,体积仅次于木星" }; public static List<Planet> getDefaultList() { List<Planet> planetList = new ArrayList<Planet>(); for (int i = 0; i < iconArray.length; i++) { planetList.add(new Planet(iconArray[i], nameArray[i], descArray[i])); } return planetList; } }
Utils
package com.example.myapplication; import android.content.Context; public class Utils { // 根据手机的分辨率从 dp 的单位 转成为 px(像素) public static int dip2px(Context context, float dpValue) { // 获取当前手机的像素密度(1个dp对应几个px) float scale = context.getResources().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); // 四舍五入取整 } // 根据手机的分辨率从 px(像素) 的单位 转成为 dp public static int px2dip(Context context, float pxValue) { // 获取当前手机的像素密度(1个dp对应几个px) float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); // 四舍五入取整 } // 根据手机的分辨率从 sp 的单位 转成为 px(像素) public static int sp2px(Context context, float spValue) { // 获取当前手机的像素密度(1个dp对应几个px) float scale = context.getResources().getDisplayMetrics().density; return (int) (spValue * scale + 0.5f); // 四舍五入取整 } // 根据手机的分辨率从 px(像素) 的单位 转成为 sp public static int px2sp(Context context, float pxValue) { // 获取当前手机的像素密度(1个dp对应几个px) float scale = context.getResources().getDisplayMetrics().density; return (int) (pxValue / scale + 0.5f); // 四舍五入取整 } }
PlanetListWithButtonAdapter
package com.example.myapplication; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.Button; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import android.widget.Toast; import java.util.List; public class PlanetListWithButtonAdapter extends BaseAdapter implements AdapterView.OnItemClickListener, AdapterView.OnItemLongClickListener { private Context mContext; // 声明一个上下文对象 private List<Planet> mPlanetList; // 声明一个行星信息列表 private int mFocusMethod; // 焦点抢占方式 // 行星适配器的构造方法,传入上下文与行星列表 public PlanetListWithButtonAdapter(Context context, List<Planet> planet_list, int focusMethod) { mContext = context; mPlanetList = planet_list; mFocusMethod = focusMethod; } // 获取列表项的个数 public int getCount() { return mPlanetList.size(); } // 获取列表项的数据 public Object getItem(int arg0) { return mPlanetList.get(arg0); } // 获取列表项的编号 public long getItemId(int arg0) { return arg0; } // 获取指定位置的列表项视图 public View getView(final int position, View convertView, ViewGroup parent) { ViewHolder holder; if (convertView == null) { // 转换视图为空 holder = new ViewHolder(); // 创建一个新的视图持有者 // 根据布局文件item_list.xml生成转换视图对象 convertView = LayoutInflater.from(mContext).inflate(R.layout.item_list_with_button, null); holder.ll_item = convertView.findViewById(R.id.ll_item); holder.iv_icon = convertView.findViewById(R.id.iv_icon); holder.tv_name = convertView.findViewById(R.id.tv_name); holder.tv_desc = convertView.findViewById(R.id.tv_desc); holder.btn_oper = convertView.findViewById(R.id.btn_oper); convertView.setTag(holder); // 将视图持有者保存到转换视图当中 } else { // 转换视图非空 // 从转换视图中获取之前保存的视图持有者 holder = (ViewHolder) convertView.getTag(); } final Planet planet = mPlanetList.get(position); holder.ll_item.setDescendantFocusability(mFocusMethod); // 设置列表项的焦点抢占方式 holder.iv_icon.setImageResource(planet.image); // 显示行星的图片 holder.tv_name.setText(planet.name); // 显示行星的名称 holder.tv_desc.setText(planet.desc); // 显示行星的描述 holder.btn_oper.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(mContext, "您点击了"+planet.name, Toast.LENGTH_LONG).show(); } }); return convertView; } // 定义一个视图持有者,以便重用列表项的视图资源 public final class ViewHolder { private LinearLayout ll_item; // 声明行星项的线性布局 public ImageView iv_icon; // 声明行星图片的图像视图对象 public TextView tv_name; // 声明行星名称的文本视图对象 public TextView tv_desc; // 声明行星描述的文本视图对象 public Button btn_oper; // 声明操作动作的按钮对象 } // 处理列表项的点击事件,由接口OnItemClickListener触发 public void onItemClick(AdapterView<?> parent, View view, int position, long id) { String desc = String.format("您点击了第%d个行星,它的名字是%s", position + 1, mPlanetList.get(position).name); Toast.makeText(mContext, desc, Toast.LENGTH_LONG).show(); } // 处理列表项的长按事件,由接口OnItemLongClickListener触发 public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { String desc = String.format("您长按了第%d个行星,它的名字是%s", position + 1, mPlanetList.get(position).name); Toast.makeText(mContext, desc, Toast.LENGTH_LONG).show(); return true; } }
================================================================================================
=================================================================================================