自定义控件——改造已有的控件——不滚动的列表视图
不滚动的列表视图
把ListView放入ScrollView会产生问题,因为ScrollView和ListView都允许滚动,那么在双方的重叠区域,上下滑动的手势究竟表示要滚动哪个视图?
Android的处理对策是:如果ListView的高度被设置为wrap_content,则此时列表视图只显示一行的高度,然后整个界面只支持滚动ScrollView。
但如此一来, ScrollView内部的ListView无法显示完整的列表。
public class NoScrollListView extends ListView { public NoScrollListView(Context context) { super(context); } public NoScrollListView(Context context, AttributeSet attrs) { super(context, attrs); } public NoScrollListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } // 重写onMeasure方法,以便自行设定视图的高度 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 将高度设为最大值,即所有项加起来的总高度 int expandSpec = MeasureSpec.makeMeasureSpec( Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); // 按照新的高度规格重新测量视图尺寸 } }
============================================================================================
布局:
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="10dp" android:text="下面是系统自带的列表视图" android:textColor="#ff0000" android:textSize="17sp" /> <ListView android:id="@+id/lv_planet" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:dividerHeight="1dp" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:padding="10dp" android:text="下面是自定义的列表视图" android:textColor="#00ff00" android:textSize="17sp" /> <!-- 自定义的不滚动列表视图,需要使用全路径 --> <com.example.myapplication.widget.NoScrollListView android:id="@+id/nslv_planet" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="10dp" android:dividerHeight="1dp" /> </LinearLayout> </ScrollView>
<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>
NoScrollListView
package com.example.myapplication.widget; import android.content.Context; import android.util.AttributeSet; import android.widget.ListView; public class NoScrollListView extends ListView { public NoScrollListView(Context context) { super(context); } public NoScrollListView(Context context, AttributeSet attrs) { super(context, attrs); } public NoScrollListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } // 重写onMeasure方法,以便自行设定视图的高度 @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 将高度设为最大值,即所有项加起来的总高度 int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); // 按照新的高度规格重新测量视图尺寸 } }
Planet
package com.example.myapplication.bean; import com.example.myapplication.R; 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; } }
PlanetListAdapter
package com.example.myapplication.adapter; 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 com.example.myapplication.R; import com.example.myapplication.bean.Planet; 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; } }
主代码:
package com.example.myapplication; import androidx.appcompat.app.AppCompatActivity; import androidx.viewpager.widget.ViewPager; import android.os.Bundle; import android.widget.ListView; import android.widget.Toast; import com.example.myapplication.adapter.ImagePagerAdapater; import com.example.myapplication.adapter.PlanetListAdapter; import com.example.myapplication.bean.GoodsInfo; import com.example.myapplication.bean.Planet; import com.example.myapplication.widget.NoScrollListView; import java.util.ArrayList; public class MainActivity extends AppCompatActivity { private ArrayList<GoodsInfo> mGoodsList; // 手机商品列表 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PlanetListAdapter adapter1 = new PlanetListAdapter(this, Planet.getDefaultList()); // 从布局文件中获取名叫lv_planet的列表视图 // lv_planet是系统自带的ListView,被ScrollView嵌套只能显示一行 ListView lv_planet = findViewById(R.id.lv_planet); lv_planet.setAdapter(adapter1); // 设置列表视图的行星适配器 lv_planet.setOnItemClickListener(adapter1); lv_planet.setOnItemLongClickListener(adapter1); PlanetListAdapter adapter2 = new PlanetListAdapter(this, Planet.getDefaultList()); // 从布局文件中获取名叫nslv_planet的不滚动列表视图 // nslv_planet是自定义控件NoScrollListView,会显示所有行 NoScrollListView nslv_planet = findViewById(R.id.nslv_planet); nslv_planet.setAdapter(adapter2); // 设置不滚动列表视图的行星适配器 nslv_planet.setOnItemClickListener(adapter2); nslv_planet.setOnItemLongClickListener(adapter2); } }