0%

高级控件与屏幕适配

本篇文章主要是记录一下高级控件ListView和CardView的使用方式和注意事项,虽然目前都已经用RecyclerView替代了ListView但是了解其中的原理和优化还是有必要的,关于ListView的原理和真正意义上的优化在后面会专门写一片文章来讲述,本篇只谈其具体使用与必须优化的方式。至于CardView其实用的还是比较多的,可以看到我的小Demo实现的效果还是很不错!最后涉及到了一些Android屏幕适配问题的解决方案。

ListView

Displays a vertically-scrollable collection of views, where each view is positioned immediatelybelow the previous view in the list. For a more modern, flexible, and performant approach to displaying lists, use android.support.v7.widget.RecyclerView.

ListView的基本使用

ListView使用步骤如下:

1、在Layout中创建ListView
2、创建每一行的layout
3、创建每一行的数据
4、用adapter将数据填充到每一行的视图中

条目布局文件 item_app_list.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="10dp"
android:orientation="horizontal">

<ImageView
android:id="@+id/app_icon_iv"
android:src="@mipmap/ic_launcher"
android:layout_width="40dp"
android:layout_height="40dp"/>

<TextView
android:id="@+id/app_name_tv"
android:textSize="20sp"
android:paddingLeft="10dp"
android:gravity="center_vertical"
android:text="@string/app_name"
android:layout_width="match_parent"
android:layout_height="40dp"/>
</LinearLayout>

AppListActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
public class AppListActivity extends AppCompatActivity {

private List<String> appNameList;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_list);

ListView listView = findViewById(R.id.app_lv);
appNameList = Arrays.asList("QQ", "微信", "牛客", "招商银行", "支付宝");

//listView.setAdapter(new AppListAdapterBase());
listView.setAdapter(new AppListAdapter(getAppInfo()));
}

// 获取所有的应用信息
private List<ResolveInfo> getAppInfo(){
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
return getPackageManager().queryIntentActivities(intent, 0);
}

public class AppListAdapter extends BaseAdapter {
private List<ResolveInfo> resolveInfoList;

public AppListAdapter(List<ResolveInfo> appInfo) {
this.resolveInfoList = appInfo;
}

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

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

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = getLayoutInflater();
convertView = layoutInflater.inflate(R.layout.item_app_list, null);
ImageView iv = convertView.findViewById(R.id.app_icon_iv);
TextView tv = convertView.findViewById(R.id.app_name_tv);
ResolveInfo resolveInfo = resolveInfoList.get(position);
tv.setText(resolveInfo.activityInfo.loadLabel(getPackageManager()));
iv.setImageDrawable(resolveInfo.activityInfo.loadIcon(getPackageManager()));

// 在这里给每一个条目设置点击事件
convertView.setOnClickListener((v) -> {
String packageName = resolveInfo.activityInfo.packageName;
String className = resolveInfo.activityInfo.name;
ComponentName componentName = new ComponentName(packageName, className);
Intent intent = new Intent();
intent.setComponent(componentName);
startActivity(intent);
});
return convertView;
}
}


// 最基础的数据展示,ImageView是固定的
public class AppListAdapterBase extends BaseAdapter {

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

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

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = getLayoutInflater();
convertView = layoutInflater.inflate(R.layout.item_app_list, null);
ImageView iv = convertView.findViewById(R.id.app_icon_iv);
TextView tv = convertView.findViewById(R.id.app_name_tv);
tv.setText(appNameList.get(position));
return convertView;
}
}
}

点击事件与长按事件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public class AppListActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_list);

ListView listView = findViewById(R.id.app_lv);
appNameList = Arrays.asList("QQ", "微信", "慕课网", "牛客");

List<ResolveInfo> resolveInfoList = getAppInfo();
listView.setAdapter(new AppListAdapter(resolveInfoList));

// 点击事件写法二
listView.setOnItemClickListener((parent, view, position, id) -> {
ResolveInfo resolveInfo = resolveInfoList.get(position);
String packageName = resolveInfo.activityInfo.packageName;
String className = resolveInfo.activityInfo.name;
ComponentName componentName = new ComponentName(packageName, className);
Intent intent = new Intent();
intent.setComponent(componentName);
startActivity(intent);
});

// 长按的事件
listView.setOnItemLongClickListener((parent, view, position, id) -> {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("提示");
builder.setMessage("确定删除吗?");
builder.setPositiveButton("确定", (dialog, which) -> {
// 移除此条目
resolveInfoList.remove(position);
listView.setAdapter(new AppListAdapter(resolveInfoList));
});
builder.setNegativeButton("取消", null);
builder.show();
return false;
});
}

// 获取所有的应用信息
private List<ResolveInfo> getAppInfo(){
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
return getPackageManager().queryIntentActivities(intent, 0);
}

public class AppListAdapter extends BaseAdapter {
private List<ResolveInfo> resolveInfoList;

public AppListAdapter(List<ResolveInfo> appInfo) {
this.resolveInfoList = appInfo;
}

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

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

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = getLayoutInflater();
convertView = layoutInflater.inflate(R.layout.item_app_list, null);
ImageView iv = convertView.findViewById(R.id.app_icon_iv);
TextView tv = convertView.findViewById(R.id.app_name_tv);
ResolveInfo resolveInfo = resolveInfoList.get(position);
tv.setText(resolveInfo.activityInfo.loadLabel(getPackageManager()));
iv.setImageDrawable(resolveInfo.activityInfo.loadIcon(getPackageManager()));
// 点击事件写法一
convertView.setOnClickListener((v) -> {
String packageName = resolveInfo.activityInfo.packageName;
String className = resolveInfo.activityInfo.name;
ComponentName componentName = new ComponentName(packageName, className);
Intent intent = new Intent();
intent.setComponent(componentName);
startActivity(intent);
});
return convertView;
}
}
}

设置HeaderView与FooterView

header_app_list.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<?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:gravity="center"
android:textSize="20sp"
android:text="This is listView's header."
android:id="@+id/header_app_list_iv"
android:background="@color/colorAccent"
android:layout_width="match_parent"
android:layout_height="80dp"/>

</LinearLayout>

AppListActivity.java(其实只需要在setAdapter之前做一个addHeaderView的操作即可)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
public class AppListActivity extends AppCompatActivity {

private List<String> appNameList;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_list);

ListView listView = findViewById(R.id.app_lv);
appNameList = Arrays.asList("QQ", "微信", "慕课网", "牛客", "招商银行");

// 增加头Header
listView.addHeaderView(getLayoutInflater().inflate(R.layout.header_app_list, null));

List<ResolveInfo> resolveInfoList = getAppInfo();
listView.setAdapter(new AppListAdapter(resolveInfoList));

// 点击事件写法二
listView.setOnItemClickListener((parent, view, position, id) -> {
ResolveInfo resolveInfo = resolveInfoList.get(position);
String packageName = resolveInfo.activityInfo.packageName;
String className = resolveInfo.activityInfo.name;
ComponentName componentName = new ComponentName(packageName, className);
Intent intent = new Intent();
intent.setComponent(componentName);
startActivity(intent);
});

// 长按的事件
listView.setOnItemLongClickListener((parent, view, position, id) -> {
AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle("提示");
builder.setMessage("确定删除吗?");
builder.setPositiveButton("确定", (dialog, which) -> {
resolveInfoList.remove(position);
listView.setAdapter(new AppListAdapter(resolveInfoList));
});
builder.setNegativeButton("取消", null);
builder.show();
return false;
});
}

// 获取所有的应用信息
private List<ResolveInfo> getAppInfo(){
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
return getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_ALL);
}

public class AppListAdapter extends BaseAdapter {
private List<ResolveInfo> resolveInfoList;

public AppListAdapter(List<ResolveInfo> appInfo) {
this.resolveInfoList = appInfo;
}

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

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

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = getLayoutInflater();
convertView = layoutInflater.inflate(R.layout.item_app_list, null);
ImageView iv = convertView.findViewById(R.id.app_icon_iv);
TextView tv = convertView.findViewById(R.id.app_name_tv);
ResolveInfo resolveInfo = resolveInfoList.get(position);
tv.setText(resolveInfo.activityInfo.loadLabel(getPackageManager()));
iv.setImageDrawable(resolveInfo.activityInfo.loadIcon(getPackageManager()));
return convertView;
}
}
}

ListView的优化

真正意义上的优化: http://www.xuanyusong.com/archives/1252

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
public class AppListActivity extends AppCompatActivity {

private List<String> appNameList;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_app_list);

ListView listView = findViewById(R.id.app_lv);
appNameList = Arrays.asList("QQ", "微信", "慕课网", "牛客", "招商银行", "支付宝");

// 增加头Header
listView.addHeaderView(getLayoutInflater().inflate(R.layout.header_app_list, null));

List<ResolveInfo> resolveInfoList = getAppInfo();
listView.setAdapter(new AppListAdapter(resolveInfoList));
}

// 获取所有的应用信息
private List<ResolveInfo> getAppInfo(){
Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
return getPackageManager().queryIntentActivities(intent, 0);
}

public class AppListAdapter extends BaseAdapter {
private List<ResolveInfo> resolveInfoList;

public AppListAdapter(List<ResolveInfo> appInfo) {
this.resolveInfoList = appInfo;
}

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

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

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if(convertView == null){
LayoutInflater layoutInflater = getLayoutInflater();
convertView = layoutInflater.inflate(R.layout.item_app_list, null);
viewHolder = new ViewHolder();
viewHolder.imageView = convertView.findViewById(R.id.app_icon_iv);
viewHolder.textView = convertView.findViewById(R.id.app_name_tv);
convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder) convertView.getTag();
}
ResolveInfo resolveInfo = resolveInfoList.get(position);
viewHolder.textView.setText(resolveInfo.activityInfo.loadLabel(getPackageManager()));
viewHolder.imageView.setImageDrawable(resolveInfo.activityInfo.loadIcon(getPackageManager()));
return convertView;
}
}

// ViewHolder
private static class ViewHolder {
public ImageView imageView;
public TextView textView;
}
}

条目布局分类加载

item_left_chat.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/time_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="21:52"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="MissingConstraints" />

<ImageView
android:id="@+id/icon_iv"
android:src="@mipmap/ic_launcher"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/time_tv"
tools:ignore="MissingConstraints"
/>

<TextView
android:id="@+id/name_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tim"
app:layout_constraintEnd_toEndOf="@+id/icon_iv"
app:layout_constraintStart_toStartOf="@+id/icon_iv"
app:layout_constraintTop_toBottomOf="@+id/icon_iv" />

<TextView
android:id="@+id/content_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, how are you?"
android:paddingStart="10sp"
app:layout_constraintBottom_toBottomOf="@+id/icon_iv"
app:layout_constraintStart_toEndOf="@+id/icon_iv"
app:layout_constraintTop_toTopOf="@+id/icon_iv"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

item_right_chat.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">

<TextView
android:id="@+id/time_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="21:52"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:ignore="MissingConstraints" />

<TextView
android:id="@+id/name_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Tim"
app:layout_constraintEnd_toEndOf="@+id/icon_iv"
app:layout_constraintStart_toStartOf="@+id/icon_iv"
app:layout_constraintTop_toBottomOf="@+id/icon_iv" />

<TextView
android:id="@+id/content_tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, how are you?"
android:paddingEnd="10sp"
app:layout_constraintBottom_toBottomOf="@+id/icon_iv"
app:layout_constraintEnd_toStartOf="@+id/icon_iv"
app:layout_constraintTop_toTopOf="@+id/icon_iv"
tools:ignore="MissingConstraints" />

<ImageView
android:id="@+id/icon_iv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@mipmap/ic_launcher"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/time_tv"
tools:ignore="MissingConstraints" />
</androidx.constraintlayout.widget.ConstraintLayout>

现在拥有item_left_chat.xml和item_right_chat.xml两个布局,所以只需要做好分类布局加载就好了:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = findViewById(R.id.main_lv);
List<ChatMessage> chatMessages = Arrays.asList(
new ChatMessage(1, 2, "Tim", "08:20", "I'm Tim.", true),
new ChatMessage(1, 2, "Tim", "08:25", "Jone, how are you?", true),
new ChatMessage(2, 1, "Jone", "08:30", "I'm fine, thinks", false),
new ChatMessage(1, 2, "Tim", "08:31", "No thinks.", true),
new ChatMessage(2, 1, "Jone", "08:32", "What can I do for you ?", false),
new ChatMessage(1, 2, "Tim", "08:59", "Please give me some money.", true)
);
listView.setAdapter(new ChatMessageAdapter(chatMessages, MainActivity.this));

}

static class ChatMessage {
public int mId;
public int mFriendId;
public String mName;
public String mDate;
public String mContent;
public boolean mIsComeMessage;

public ChatMessage(int mId, int mFriendId,
String mName, String mDate,
String mContent, boolean mIsComeMessage) {
this.mId = mId;
this.mFriendId = mFriendId;
this.mName = mName;
this.mDate = mDate;
this.mContent = mContent;
this.mIsComeMessage = mIsComeMessage;
}
}

static class ChatMessageAdapter extends BaseAdapter {
List<ChatMessage> chatMessages;
Context context;

interface IMessageViewType {
int COM_MESSAGE = 1;
int TO_MESSAGE = 2;
}

public ChatMessageAdapter(List<ChatMessage> chatMessages, Context context) {
this.chatMessages = chatMessages;
this.context = context;
}

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

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

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
ChatMessage chatMessage = chatMessages.get(position);
if(convertView == null){
if(chatMessage.mIsComeMessage){
convertView = layoutInflater.inflate(R.layout.item_left_chat, null);
}else{
convertView = layoutInflater.inflate(R.layout.item_right_chat, null);
}
TextView timeTv = convertView.findViewById(R.id.time_tv);
ImageView iconIv = convertView.findViewById(R.id.icon_iv);
TextView nameTv = convertView.findViewById(R.id.name_tv);
TextView contentTv = convertView.findViewById(R.id.content_tv);
timeTv.setText(chatMessage.mDate);
nameTv.setText(chatMessage.mName);
contentTv.setText(chatMessage.mContent);
}
return convertView;
}

@Override
public int getItemViewType(int position) {
ChatMessage chatMessage = chatMessages.get(position);
return chatMessage.mIsComeMessage ?
IMessageViewType.COM_MESSAGE : IMessageViewType.TO_MESSAGE;
}

@Override
public int getViewTypeCount() {
return 2; // IMessageViewType 两种类型
}
}
}

CardView

CardView是用于实现卡片式布局效果的重要控件,实际上也是一个FrameLayout,只是额外提供了圆角和阴影,看上去有立体效果。

CardView是什么?

  1. Android5.0之后新增
  2. com.android.support:cardview-v7:26.1.0独立引入
  3. 继承自FrameLayout,方便作为其他控件容器,添加3D阴影和圆角效果

CardView常用属性

  1. cardBackgroundColor设置背景色
  2. cardCornerRadius设置圆角半径
  3. contentPadding 设置内部padding
  4. cardElevation设置阴影大小
  5. cardUseCompatPadding 默认为false,用于5.0及以上,true则添加额外的padding绘制阴影
  6. cardPreventCornerOverlap 默认为true,用于5.0以下,添加额外的padding,防止内容和圆角重叠

CardView的常用属性

引入CardView的依赖

1
implementation 'androidx.cardview:cardview:1.0.0'

一个VardView的小Demo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">

<!--
cardBackgroundColor:设置背景色
cardCornerRadius:设置圆角
cardElevation:设置阴影
contentPadding:设置内部padding
-->
<androidx.cardview.widget.CardView
android:layout_gravity="center"
app:cardBackgroundColor="@color/colorAccent"
app:cardCornerRadius="10dp"
app:cardElevation="5dp"
app:contentPadding="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<TextView
android:layout_width="200dp"
android:layout_height="50dp"
android:text="Hello World!"
android:gravity="center"
android:layout_gravity="center"
/>
</androidx.cardview.widget.CardView>
</FrameLayout>

其实可以看到CardView还真的是有种卡片效果的。常用的属性如下:

属性 作用
card_view:cardElevation 阴影的大小
card_view:cardMaxElevation 阴影最大高度
card_view:cardBackgroundColor 卡片的背景色
card_view:cardCornerRadius 卡片的圆角大小
card_view:contentPadding 卡片内容于边距的间隔
card_view:contentPaddingBottom 卡片内容与底部的边距
card_view:contentPaddingTop 卡片内容与顶部的边距
card_view:contentPaddingLeft 卡片内容与左边的边距
card_view:contentPaddingRight 卡片内容与右边的边距
card_view:contentPaddingStart 卡片内容于边距的间隔起始
card_view:contentPaddingEnd 卡片内容于边距的间隔终止
card_view:cardUseCompatPadding 设置内边距,V21+的版本和之前的版本仍旧具有一样的计算方式
card_view:cardPreventCornerOverlap 在V20和之前的版本中添加内边距,这个属性为了防止内容和边角的重叠

CardView使用示例

先把展示的图片发放在drawable下的xxhdpi下:分别是img01-img05。

item_msg.xml ,这是ListView的条目布局:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?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="wrap_content"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">

<androidx.cardview.widget.CardView
app:cardCornerRadius="8dp"
app:cardElevation="5dp"
app:cardUseCompatPadding="false"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/item_icon_iv"
android:scaleType="centerCrop"
tools:src="@drawable/img01"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

<TextView
android:id="@+id/item_title_tv"
android:layout_margin="8dp"
android:textColor="#000000"
android:textSize="16sp"
android:textStyle="bold"
tools:text="一起来学习Android技术"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

<TextView
android:id="@+id/item_content_tv"
android:layout_margin="8dp"
android:textColor="#000000"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginBottom="8dp"
tools:text="一起来学习Android技术, 一起来学习Android技术。"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
</androidx.cardview.widget.CardView>
</FrameLayout>

main_activity.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/id_listview_msg_list"
android:divider="@null"
android:background="#ffffff"
android:paddingTop="8dp"
tools:context=".MainActivity">


</ListView>

MainActivity.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
public class MainActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setTitle("CardView测试");
setContentView(R.layout.activity_main);
ListView msgListView = findViewById(R.id.id_listview_msg_list);

List<Msg> messageList = Arrays.asList(
new Msg(1, R.drawable.img01, "如何才能不错过人工智能的时代?", "下一个时代就是机器学习的时代,与你一起预见未来!"),
new Msg(2, R.drawable.img02, "关于你的面试、实习心路历程", "奖品丰富,更设有参与奖,随机抽取5名幸运用户,获得付费面试课程中的任意一门!"),
new Msg(3, R.drawable.img03, "狗粮不是你想吃,就能吃的!", "你的朋友圈开始了吗?一半秀恩爱,一半扮感伤!不怕,陪你坚强地走下去!"),
new Msg(4, R.drawable.img04, "前端跳槽面试那些事儿~", "工作有几年了,项目偏简单有点拿不出手怎么办?目前还没毕业,正在自学前端,请问可以找到一份前端工作吗,我该怎么办?"),
new Msg(5, R.drawable.img05, "图解程序员怎么过七夕?", "图解程序员怎么过七夕,哈哈哈哈,活该单身25年!")
);

msgListView.setAdapter(new MoocAdapter(messageList));
}

class MoocAdapter extends BaseAdapter {
private List<Msg> msgList;

public MoocAdapter(List<Msg> msgList) {
this.msgList = msgList;
}

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

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

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
if(convertView == null){
convertView = View.inflate(MainActivity.this, R.layout.item_msg, null);
//convertView = getLayoutInflater().inflate(R.layout.item_msg, null);
viewHolder = new ViewHolder();
viewHolder.iconImageView = convertView.findViewById(R.id.item_icon_iv);
viewHolder.titleTextView = convertView.findViewById(R.id.item_title_tv);
viewHolder.contentTextView = convertView.findViewById(R.id.item_content_tv);
convertView.setTag(viewHolder);
}else{
viewHolder = (ViewHolder) convertView.getTag();
}
Msg msg = msgList.get(position);
viewHolder.contentTextView.setText(msg.getContent());
viewHolder.titleTextView.setText(msg.getTitle());
viewHolder.iconImageView.setImageResource(msg.getImgResId());

return convertView;
}
}

static class ViewHolder {
ImageView iconImageView;
TextView titleTextView;
TextView contentTextView;
}
}

@Data
@AllArgsConstructor
@NoArgsConstructor
class Msg {
private int id;
private int imgResId;
private String title;
private String content;
}

看看最终的展示效果:

这里用到了lombok这款插件,那就顺便说一下lombok在Android开发中如何使用吧。

使用tools工具预览

1
2
3
4
5
6
7
8
9
10
11
12
<!-- 引入tools命令空间 -->
xmlns:tools="http://schemas.android.com/tools"

<TextView
android:id="@+id/item_title_tv"
android:layout_margin="8dp"
android:textColor="#000000"
android:textSize="16sp"
android:textStyle="bold"
tools:text="一起来学习Android技术"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>

使用tools工具可以进行控件效果预览,如果直接写了android:text=XXX,那么很可能在数据无法正确加载的情况下显示出我们定义的android:text属性,所以无论是ImageView还是TextView等需要添加数据才可以预览的情况都可以使用tools这个命名空间。

Lombok在Android中的使用

引入lombok的依赖:

1
2
3
4
5
6
// 这是CardView的依赖
implementation 'androidx.cardview:cardview:1.0.0'
// 配置Lombok注解处理器
annotationProcessor group: 'org.projectlombok', name: 'lombok', version: '1.18.12'
// 声明lombok的依赖作用域
compileOnly(group: 'org.projectlombok', name: 'lombok', version: '1.18.12')

安装Lombok的插件:

安装好插件后重启AndroidStudio即可使用Lombok插件。

最后别忘了在moudle或者project底下新建一个lombok.config:

1
lombok.anyConstructor.suppressConstructorProperties=true

然后开始愉快的写代码吧 ~

编译时编码错误解决方案

1
2
3
4
5
6
7
8
9
10
android {
compileSdkVersion ...
buildToolsVersion "..."

// 指定编码为UTF-8
compileOptions {
encoding "UTF-8"
}
...
}

Android屏幕适配

这个专门放置一篇博客!

  • 本文作者: Tim
  • 本文链接: https://zouchanglin.cn/4053989668.html
  • 版权声明: 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!