1.任务1下拉列表Spinner
本任务的演示效果如图所示。在该应用中,视图根节点为垂直的线性布局,依次放置3 个Ul:文本框 TextView (显示个人信息);文本框 TextView (id 为 tv result)用于显示下拉列表的选择结果;下拉列表 Spinner,有3 个可供选择的数据“杭州”“宁波”和“温州”,点击 Spinner,会弹出下拉列表显示候选数据,选中某项数据后,下拉列表会收缩,tv result 则显示选中的数据。
实现
1.应用布局 my_main.xml
布局以垂直 LinearLayout 为根节点,依次拖入1个 TextView,显示个人信息;1个 TextView,将 id 改为 tv_result,用于显示下拉列表的选择结果;从 Container 里找到 Spinner,拖入容器,若 UI面板里找不到,可点击搜索符号搜索。拖入的 Spinner 默认高度是 wrap_content,在组件树视图中有感叹号警示,其原因是包围内容时其高度是 24dp,认为在高密度屏幕中尺寸过小,不利于触控操作,并建议通过 android:minHeight 属性将最小高度设置为 48dp,即使读者接受 fix 按钮的修改建议,增加了对应属性,该 UI依然有警示符号,提醒当前 Spinner 内容为空。这些警示只是 Android的一些建议,不会引起程序崩溃,可忽略。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Your name and ID" />
<TextView
android:id="@+id/tv_result"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="" />
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
2.MainActivity.java
活动页面 MainActivity 的程序如代码 3-2 所示。在oCreate0)中初始化了1个 String 数组,作为适配器构造方法的数据。适配器声明时采用了泛型 ArrayAdapter<String>,表示该适配器只接受 String 类型的数组或列表数据,当调用适配器的 getitem0)方法获取指定位置数据时,其返回的数据就是泛型指定的类型(本例中是 String 类型)。返回的数据显示到TextView中。
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
Spinner sp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_main);
final TextView tv=findViewById(R.id.tv_result);
final String[] cities=new String[]{"杭州","宁波","温州"};
ArrayAdapter<String> adapter=new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1,cities);
sp=findViewById(R.id.spinner);
sp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
String city=cities[i];
tv.setText(city);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
sp.setAdapter(adapter);
}
}
2.任务2-使用下拉列表控制文本颜色
本任务图 3-6 所示,根节点为垂直的线性布局,有 1个 TextView (id 为 tv),显示个人信息;1个下拉列表 Spinner,下拉列表中有 XML 资源文件中自定义的 3 个颜色数据“红色”“绿色”和“蓝色”,点击选项,则文本框 tv 的文本颜色更改为对应的颜色。
本任务要求颜色的值和颜色的名称均定义在 XML 资源文件里,方便后期替换更改,完成项目后,在不更改任何代码的情况下,将资源文件里的颜色名称和颜色值修改为 4 个数据运行应用观察是否正常。资源文件在项目的 res/values 文件夹下,可以是自定义 XML 文件,也可以在项目所生成的默认文件上增加数据。
实现
使用XML资源管理文字、颜色常量值,在项目res/values文件夹下的XML文件中,用color标签定义颜色,string标签定义字符串,dimen定义尺寸,drawable定义绘图资源及图片,integer定义整形数据。
除了单个数据的定义,XML 中还可以定义数组,有 string-array 定义字符串数组:integer-array 定义整型数组;array 定义通用数组。数组除了 name 属性,还有<item>子节点定义的是数组中1个元素的值,该值可直接赋值也可以使用引用。数组在代码中的引用方式是R.arrayname,其中name 是数组的name属性值,在程序中获取XML资源还要利用Activity对象提供的 getResources()方法获得 Resources 对象,使用 Resources 对象对 XML资源取数,若是 string-array 数据,可用 getStringArray0方法;若是nteger-array,则用 getIntArray0方法:若是通用型 array,则需通过 obtainTypedArray0获得通用型数组,再通过 TypedArray 对象的getColor0、getDimension0、getDrawable0等方法分别进行颜色、尺寸或绘图对象的二次解析。
res/values/colors.xml文件增加颜色数组和颜色名称数组
<array name="my_color_values">
<item>#f00</item>
<item>#0f0</item>
<item>#00f</item>
</array>
<string-array name="my_color_names">
<item>红色</item>
<item>绿色</item>
<item>蓝色</item>
</string-array>
my_main.xml 文件布局
布局中 TextView 需要一个 id 值,以便于在代码中对其进行属性控制。
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Your name and ID" />
<Spinner
android:id="@+id/spinner2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
MainActivity.java
注意 colorNames 和colorValues 的处理方式,获取颜色名称数组和获取颜色值数组方式的不同。颜色名称数组 colorNames 是string-array 类型,可直接通过 Resources 对象的 getStringArray0方法获得;颜色值数组colorValues 是通用型array,需通过 obtainTypedArray0方法得到 TypedArray 数组,再通过对应的方法 getColor()进行二次解析。
import androidx.appcompat.app.AppCompatActivity;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
import java.lang.reflect.Type;
public class MainActivity extends AppCompatActivity {
Spinner sp;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_main2);
final TextView tv=findViewById(R.id.textView);
Resources res=getResources(); //XML中定义的资源数据需要Resources对象来获取
Spinner sp=findViewById(R.id.spinner);
// String数组可直接调用getStringArray()方法获取
String[] colorNames=res.getStringArray(R.array.my_color_names);
//资源没有提供颜色数组,只能通过数组TypedArray获取数组对象
final TypedArray colorValues=res.obtainTypedArray(R.array.my_color_values);
ArrayAdapter<String> adapter=new ArrayAdapter<>(this,
android.R.layout.simple_list_item_1,colorNames);
sp.setAdapter(adapter);
sp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
int color =colorValues.getColor(i, Color.BLACK);
//第2个参数是获取失败时替换的默认颜色
//第2个参数使用了Color类中提供的颜色常量,Color.BLACK为黑色
tv.setTextColor(color);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
}
}
3.任务3-使用下拉列表控制文本大小
布局根节点为垂直的线性布局,依次有1个 TextView(id 为 tv),显示个人信息;1 个下拉列表 Spinner,Spinner 中有下拉选项“小号”、“中号”和“大号”,对应字体大小的值 10sp、16sp 和 24sp,Spinner 选择字号,则tv 的字体大小更改为对应字号值。字体大小的值数组和名称数组采用 XML 资源管理,并且需要利用 Logcat打印输出字号值对应的真实值。
实现
在res/values的文件目录创建dimens.xml文件,和任务2一样
<?xml version="1.0" encoding="utf-8"?>
<resources>
<array name="my_dimen_values">
<item>10sp</item>
<item>16sp</item>
<item>24sp</item>
</array>
<string-array name="my_dimen_names">
<item>小号</item>
<item>中号</item>
<item>大号</item>
</string-array>
</resources>
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_main);
Spinner sp=findViewById(R.id.spinner);
final TextView tv=findViewById(R.id.tv_result);
Resources res=getResources();
String[] dimNames=res.getStringArray(R.array.myDimenNames);
final TypedArray dims=res.obtainTypedArray(R.array.myDimens);
ArrayAdapter<String> adapter=new ArrayAdapter<>(
this,android.R.layout.simple_list_item_1,dimNames);
sp.setAdapter(adapter);
sp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
float dim=dims.getDimension(i, 10.0f);
tv.setTextSize(dim);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
}
}
4.任务4-使用 ListView 切换ImageView 图片
视图根节点为垂直的 LinarLayout,依次有 1个TextView,显示个人信息;1 个图片视图 ImageView,高和宽分别为 200dp; 1 个列表视图ListView,显示“杭州”、“宁波”和“温州”。点击 ListView 的列表项,ImageView 图片切换为对应城市的图片。
实现
拷贝图片时,可单击 res/drawable 文件夹,直接 ctrtv 快捷键粘贴图片,即可将复制的图片文件拷贝至 drawable 文件夹,并得到图 3-13 的文件夹选择提示,有 drawable 和drawable-v24,如果你的虚拟机是 Android5.0 (API21),不能选择rawable-v24 文件夹,否则会出现找不到图片资源导致的程序崩溃。Android 设备不同屏幕有不同的屏幕密度,为了更好地匹配效果,会有 mipmap-hdpi,mipmap-mdpi,mipmap-xhdpi 等不同的文件夹匹配不同尺寸的图片使得设备能根据屏幕密度选择合适文件夹下的图片资源以获得更好的显示效果。同理,drawable-v24 文件夹是为了适配 Android7.0 以上设备 (API24),若虚拟机是Android5.0,则访问不了该文件夹下的图片资源,但是 Android7.0 以上设备同样可以访问drawable 文件夹。拷贝图片时,虽然开发环境默认选择 drawable-v24 文件夹,我们需要手动切换到 drawable 文件夹,以便低于 Android7.0 的设备能访问到图片资源。
布局文件my_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Your name and ID" />
<ImageView
android:id="@+id/imageView"
android:layout_width="200dp"
android:layout_height="200dp"
app:srcCompat="@drawable/hangzhou" />
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
MianActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.Spinner;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
int[] picIds=new int[]{R.drawable.hangzhou,R.drawable.ningbo,R.drawable.wenzhou};
String[] picNames=new String[]{"杭州","宁波","温州"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_main);
ListView lv=findViewById(R.id.listView);
final ImageView iv=findViewById(R.id.imageView);
ArrayAdapter<String> adapter=new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,picNames);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
int pic=picIds[i];
iv.setImageResource(pic);
}
});
}
}
5任务5-使用SimpleAdapter生成复视图
布局根节点为垂直的 LinearLayout,有 1个 TextView 显示个人信息;1 个 ListView,显示城市信息。ListView 一行的视图较为复杂,左边是 1个mageVicw,宽和高均为 80dp,采用中心点裁剪,使得不同尺寸的图片在 ListView 中均具有相同的宽高;中间是 2个 TextView 上下放置,文字与左图有 10dp 的间距,上部 TextView显示城市名称,黑色字体,字号大小为 20sp,下部 TextView 显示热线电话,默认字体;右边是一个拨打电话图标。点击 ListView 列表项,会弹出 Toast,显示点击的城市名称。
ListVicw 不再是简单的一行行文字显示,而是采用自定义的布局实现特定格式的样式,并具有相对复杂的数据。在本例中,一行视图具有图片资源、城市名称和电话号码等数据,需要自定义视图进行数据渲染。直接采用 ArrayAdapter 以及内置的样式实现不了本任务的效果,可通过SimpleAdapter 实现。
实现
布局文件my_mian.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Your name and ID" />
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
row_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/row_view_iv"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
app:srcCompat="@drawable/chenlong" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"xm
android:paddingLeft="10dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/row_view_tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:text="Name" />
<TextView
android:id="@+id/row_view_tv_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:text="Phone" />
</LinearLayout>
<ImageView
android:id="@+id/row_view_iv_call"
android:layout_width="40dp"
android:layout_height="40dp"
app:srcCompat="@android:drawable/stat_sys_phone_call" />
</LinearLayout>
MianActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import java.util.ArrayList;
import java.util.HashMap;
public class MainActivity extends AppCompatActivity {
public static final String KEY_NAME="key_name";
public static final String KEY_PHONE="key_phone";
public static final String KEY_PIC_ID="key_pic_id";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_main);
ListView lv = findViewById(R.id.listView);
String[] names = new String[]{"Lixiaolong", "Chenlong", "Lilianjie"};
String[] phones = new String[]{"1111", "2222", "3333"};
int[] picIds = new int[]{R.drawable.lixiaolong, R.drawable.chenlong, R.drawable.lilianjie};
final ArrayList<HashMap<String, Object>> list = new ArrayList<>();
for (int i = 0; i < names.length; i++) {
HashMap<String, Object> hashMap = new HashMap<>();
hashMap.put(KEY_NAME, names[i]);
hashMap.put(KEY_PHONE, phones[i]);
hashMap.put(KEY_PIC_ID, picIds[i]);
list.add(hashMap);
}
SimpleAdapter adapter = new SimpleAdapter(this, list, R.layout.row_view, new String[]{KEY_NAME, KEY_PHONE, KEY_PIC_ID},
new int[]{R.id.row_view_tv_name, R.id.row_view_tv_phone, R.id.row_view_iv});
lv.setAdapter(adapter);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
HashMap<String, Object> hashMap = list.get(position);
Toast.makeText(MainActivity.this, hashMap.get(KEY_NAME).toString(), Toast.LENGTH_LONG).show();
}
});
}
}
6.任务6-改写ArrayAdapter生成复杂视图
其主布局和自定义行视图均与任务 5 相同,但增加了更多的逻辑处理。当某城市数据无电话号码时,则无对应的拨打图标:点击列表项,用 Toast显示城市名称:点击拨打图标,则 Toast 显示电话号码。为了使得 ListView 能滚动,城市数据多于一屏幕能显示的数据数目(为了减少代码,数据可重复)。
实现
布局文件my_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Your name and ID" />
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
row_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/row_view_iv"
android:layout_width="100dp"
android:layout_height="100dp"
android:scaleType="centerCrop"
app:srcCompat="@drawable/chenlong" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:paddingLeft="10dp"
android:layout_weight="1"
android:orientation="vertical">
<TextView
android:id="@+id/row_view_tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:text="Name" />
<TextView
android:id="@+id/row_view_tv_phone"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="24sp"
android:text="Phone" />
</LinearLayout>
<ImageView
android:id="@+id/row_view_iv_call"
android:layout_width="40dp"
android:layout_height="40dp"
app:srcCompat="@android:drawable/stat_sys_phone_call" />
</LinearLayout>
PhoneData.java
public class PhoneData {
private String name;
private String phone;
private int picId;
public PhoneData(String name, String phone, int picId) {
this.name = name;
this.phone = phone;
this.picId = picId;
}
public String getName() {
return name;
}
public String getPhone() {
return phone;
}
public int getPicId() {
return picId;
}
public void setName(String name) {
this.name = name;
}
public void setPhone(String phone) {
this.phone = phone;
}
public void setPicId(int picId) {
this.picId = picId;
}
}
PhoneDataAdapter.java
import android.content.Context;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
public class PhoneDataAdapter extends ArrayAdapter<PhoneData> {
private Context context;
private ArrayList<PhoneData> list;
public PhoneDataAdapter(@NonNull Context context,ArrayList<PhoneData> list) {
super(context, android.R.layout.simple_list_item_1,list);
this.context=context;
this.list=list;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View v=convertView;
if(v==null){
v= LayoutInflater.from(context).inflate(R.layout.row_view,null,false);
}
TextView tv_name= v.findViewById(R.id.row_view_tv_name);
TextView tv_phone=v.findViewById(R.id.row_view_tv_phone);
ImageView iv=v.findViewById(R.id.row_view_iv);
ImageView iv_call=v.findViewById(R.id.row_view_iv_call);
PhoneData phoneData=list.get(position);
final String phone=phoneData.getPhone();
tv_phone.setText(phone);
tv_name.setText(phoneData.getName());
iv.setImageResource(phoneData.getPicId());
if(TextUtils.isEmpty(phone)){
iv_call.setVisibility(View.GONE);
}else{
iv_call.setVisibility(View.VISIBLE);
}
iv_call.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context,"Calling:"+phone,Toast.LENGTH_LONG).show();
}
});
return v;
}
}
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_main);
ListView lv = findViewById(R.id.listView);
final ArrayList<PhoneData> list=new ArrayList<>();
list.add(new PhoneData("Lixiaolong","",R.drawable.lixiaolong));
list.add(new PhoneData("Chenlong","1111",R.drawable.chenlong));
list.add(new PhoneData("Lilianjie","2222",R.drawable.lilianjie));
PhoneDataAdapter adapter=new PhoneDataAdapter(this,list);
lv.setAdapter(adapter);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
PhoneData phoneData=list.get(position);
Toast.makeText(MainActivity.this, phoneData.getName(), Toast.LENGTH_LONG).show();
}
});
}
}
7.任务7-使用网格视图GridView
视图根节点为垂直的 LinearLayout,依次有 1个TextView,显示个人信息;1 个 TextView 用于显示网格视图 GridView 的点击结果;1 个GridView,具有 3 列,每列等宽,使得 3 列占据屏幕的整个宽度。GridView 的数据使用循环程序生成100 个数据,依次为“Item00”至“Item99”
1.GridView 简介
GridView 的使用方法与 ListView 类似,需要适配器,相应的点击事件亦与 ListView 相同。GridView 作为网格视图,支持一行显示多列,行中每列数据对应数据源的一个数据,因此网格视图往往能比 ListView 显示得更紧凑。
在布局中,GridView 相比 ListView 需要使用更多的属性,例如:
- android:numColumns 控制 GridView 的列数,属性值可以是直接的数字,表示有多少列,也可以是 auto fit,由系统根据 GridView 的列宽和父容器宽度自动计算列数。
- android:columnWidth 控制列宽,如果android:numColumns 有确定的数值,列宽属性一般上不再使用。
- android:gravity 用于控制 GridView 单元格内的对齐方式
- android:horizontalSpacing 和 android:verticalSpacing 分别控制单元格之间的水平间距(列间距)和垂直间距(行间距)。
- android:stretchMode 控制 GridView 分配完列空间后,对剩余空间的处理方式,若属性值是 none,则剩余空间不扩展到列宽或列间距上;若属性值为 spacingWidth,则将剩余空间分配到列间距上,但是首列左边和末列右边不分配:若属性值为 columnWidth,则将剩余空间平均分配到列宽上;若属性值为 spacingWidthUniform,则将剩余空间分配到列间距上,且首列左边和末列右边均分配。
2.布局文件
my_mian.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Your name and ID" />
<TextView
android:id="@+id/tv_grid_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<GridView
android:id="@+id/gridView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="3"
android:stretchMode="columnWidth"/>
</LinearLayout>
MianActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.GridView;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_main);
GridView gv=findViewById(R.id.gridView);
final TextView tv=findViewById(R.id.tv_grid_view);
final ArrayList<String> list=new ArrayList<>();
for(int i=0;i<99;i++){
String s=String.format("Item%02d",i);
list.add(s);
}
ArrayAdapter<String> adapter=new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,list);
gv.setAdapter(adapter);
gv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
tv.setText(list.get(position));
}
});
}
}
8.任务8列与网图的动态切换
根节点为垂直线性布局,依次有1个 TextView,显示个人信息:1个Switch 开关,控制视图类型:1个ListView 或者 GridView。
Switch 开关对视图的控制逻辑为:当 Switch 开关处于开的状态,则为列表视图,且 Switch 开关的文字为“列表视图”:当 Switch 开关处于关的状态,则为网格视图,且开关的文字为“网格视图”。
实现
1.布局文件
任务中 ListView 和 GridView 对应的单元视图文件略有不同,例如 ListView 中各 UI是左右关系,GridView 中则是上下关系,因此适配器所需的视图分开设计。ListView 适配器的单元视图文件 row_view list_view.xml 如代码 3-22 所示,UI为左右布局;GridView 适配器的单元视图文件 row_view_grid_vicw.xml 如代码 3-23 所示,UI为上下布局。两个布局文件对应的 UI 使用了相同的 id,以方便使用同一个适配器
my_mian.xml
<?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">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Your name and ID" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Your name and ID" />
<GridView
android:id="@+id/gridView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:numColumns="2"
android:stretchMode="columnWidth" />
</LinearLayout>
row_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical">
<ImageView
android:id="@+id/row_view_iv"
android:layout_width="130dp"
android:layout_height="130dp"
android:scaleType="centerCrop"
app:srcCompat="@drawable/da_luo_shan" />
<TextView
android:id="@+id/row_view_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="landscape" />
</LinearLayout>
Landscape.java
public class Landscape {
private String name;
private int picId;
public Landscape(String name, int picId) {
this.name = name;
this.picId = picId;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getPicId() {
return picId;
}
public void setPicId(int picId) {
this.picId = picId;
}
}
LandscapeAdapter.java
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
public class LandscapeAdapter extends ArrayAdapter<Landscape> {
private Context context;
private ArrayList<Landscape> list;
public LandscapeAdapter(@NonNull Context context,ArrayList<Landscape> list) {
super(context,android.R.layout.simple_list_item_1,list);
this.list=list;
this.context=context;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
View v=convertView;
if(v==null){
v= LayoutInflater.from(context).inflate(R.layout.row_view,null,false);
}
TextView row_tv=v.findViewById(R.id.row_view_tv);
ImageView row_iv=v.findViewById(R.id.row_view_iv);
Landscape landscape=list.get(position);
row_tv.setText(landscape.getName());
row_iv.setImageResource(landscape.getPicId());
return v;
}
}
MainActivity.java
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.TextView;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_main);
GridView gv=findViewById(R.id.gridView);
final TextView tv=findViewById(R.id.textView);
ArrayList<Landscape> list=new ArrayList<>();
for(int i=0;i<4;i++){
list.add(new Landscape("Daluo Mountain",R.drawable.da_luo_shan));
list.add(new Landscape("Nanxi River",R.drawable.nan_xi_river));
list.add(new Landscape("Yandang Mountain",R.drawable.yan_dang_shan));
}
final LandscapeAdapter adapter=new LandscapeAdapter(this,list);
gv.setAdapter(adapter);
gv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Landscape landscape=adapter.getItem(position);
tv.setText(landscape.getName());
}
});
}
}
BigTask2
编写一个程序,包含2个TextView(其中一个显示学号姓名),1个Spinner,1个ImageView,1个ListView。Spinner中的内容是城市名称(不少于3个),ListView中的内容是Spinner所选中城市的风景点(响应Spinner的选择事件),TextView显示Spinner的城市和风景点名称(响应ListView的ItemClick事件),ImageView显示所选中城市的风景照。ListView适配器需要改写,使之在一行能显示风景图片和文字。
实现
布局文件
my_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Your name and ID" />
<TextView
android:id="@+id/tv_info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="TextView" />
<Spinner
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<ImageView
android:id="@+id/imageView"
android:layout_width="150dp"
android:layout_height="150dp"
app:srcCompat="@drawable/great_wall" />
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
my_row.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:gravity="center_vertical"
android:padding="5dp"
android:layout_height="wrap_content">
<TextView
android:id="@+id/row_view_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:layout_weight="1"
android:text="Name" />
<ImageView
android:id="@+id/row_view_iv"
android:layout_width="60dp"
android:layout_height="60dp"
android:scaleType="centerCrop"
app:srcCompat="@drawable/da_luo_shan" />
</LinearLayout>
LandspcaeDataCenter.java
public class LandspaceDataCenter {
private ArrayList<ArrayList<Landspace>> allLandspaceList;
private ArrayList<Landspace> currentLandscapeList;
private ArrayList<String> cityList;
private int currentCityPosition;
private String currentCityName;
public LandspaceDataCenter(){
initData();
}
private void initData(){
allLandspaceList=new ArrayList<>();
cityList=new ArrayList<>();
ArrayList<Landspace> wenzhou=new ArrayList<>();
wenzhou.add(new Landspace("Daluo mountain",R.drawable.da_luo_shan));
wenzhou.add(new Landspace("Nanxi River",R.drawable.nan_xi_river));
wenzhou.add(new Landspace("Yandang Mountain",R.drawable.yan_dang_shan));
insertLandspaceList("Wenzhou",wenzhou);
ArrayList<Landspace> hangzhou=new ArrayList<>();
hangzhou.add(new Landspace("West lake",R.drawable.west_lake));
hangzhou.add(new Landspace("Qianjiang CBD",R.drawable.qian_jiang_cbd));
hangzhou.add(new Landspace("Lingyin Temple",R.drawable.qian_jiang_cbd));
insertLandspaceList("Hangzhou",hangzhou);
ArrayList<Landspace> beijing=new ArrayList<>();
beijing.add(new Landspace("The Imperial Palace",R.drawable.the_imperial_palace));
beijing.add(new Landspace("Great Wall",R.drawable.great_wall));
beijing.add(new Landspace("Olympic Sports Center",R.drawable.olympic_sports_center));
insertLandspaceList("Beijing",beijing);
}
private void insertLandspaceList(String cityName, ArrayList<Landspace> landspacesList) {
cityList.add(cityName);
allLandspaceList.add(landspacesList);
}
public ArrayList<Landspace> updateLandscapeList(int position){
currentCityPosition=position;
currentLandscapeList=allLandspaceList.get(currentCityPosition);
currentCityName=cityList.get(currentCityPosition);
return currentLandscapeList;
}
public ArrayList<Landspace> getCurrentLandscapeList() {
return currentLandscapeList;
}
public ArrayList<String> getCityList() {
return cityList;
}
public int getCurrentCityPosition() {
return currentCityPosition;
}
public String getCurrentCityName(){
return currentCityName;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
LandspaceDataCenter dataCenter;
TextView tv;
ImageView iv;
ListView lv;
LandspaceAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_main);
Spinner sp=findViewById(R.id.spinner);
lv=findViewById(R.id.listView);
tv=findViewById(R.id.tv_info);
iv=findViewById(R.id.imageView);
dataCenter=new LandspaceDataCenter();
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
updateListViewItem(i);
}
});
ArrayAdapter<String> spAdapter=new ArrayAdapter<>(this,android.R.layout.simple_list_item_1,dataCenter.getCityList());
sp.setAdapter(spAdapter);
sp.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) {
updateListView(i);
}
@Override
public void onNothingSelected(AdapterView<?> adapterView) {
}
});
}
private void updateListView(int i) {
ArrayList<Landspace> list=dataCenter.updateLandscapeList(i);
adapter=new LandspaceAdapter(this,list);
lv.setAdapter(adapter);
updateListViewItem(0);
}
private void updateListViewItem(int i) {
Landspace landspace=(Landspace) adapter.getItem(i);
tv.setText(dataCenter.getCurrentCityName()+":"+landspace.getName());
iv.setImageResource(landspace.getPicId());
}
}
Landscape.java和LandscapeAdapter.java 代码和任务8基本一致