高级控件与屏幕适配

本篇文章主要是记录一下高级控件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<?xml version="1.0" encoding="utf-8"?>
 2<LinearLayout
 3    xmlns:android="http://schemas.android.com/apk/res/android"
 4    xmlns:tools="http://schemas.android.com/tools"
 5    android:layout_width="match_parent"
 6    android:layout_height="match_parent"
 7    android:padding="10dp"
 8    android:orientation="horizontal">
 9
10    <ImageView
11        android:id="@+id/app_icon_iv"
12        android:src="@mipmap/ic_launcher"
13        android:layout_width="40dp"
14        android:layout_height="40dp"/>
15
16    <TextView
17        android:id="@+id/app_name_tv"
18        android:textSize="20sp"
19        android:paddingLeft="10dp"
20        android:gravity="center_vertical"
21        android:text="@string/app_name"
22        android:layout_width="match_parent"
23        android:layout_height="40dp"/>
24</LinearLayout>

AppListActivity.java

 1public class AppListActivity extends AppCompatActivity {
 2
 3    private List<String> appNameList;
 4
 5    @Override
 6    protected void onCreate(Bundle savedInstanceState) {
 7        super.onCreate(savedInstanceState);
 8        setContentView(R.layout.activity_app_list);
 9
10        ListView listView = findViewById(R.id.app_lv);
11        appNameList = Arrays.asList("QQ", "微信", "牛客", "招商银行", "支付宝");
12
13        //listView.setAdapter(new AppListAdapterBase());
14        listView.setAdapter(new AppListAdapter(getAppInfo()));
15    }
16
17    // 获取所有的应用信息
18    private List<ResolveInfo> getAppInfo(){
19        Intent intent = new Intent(Intent.ACTION_MAIN, null);
20        intent.addCategory(Intent.CATEGORY_LAUNCHER);
21        return getPackageManager().queryIntentActivities(intent, 0);
22    }
23
24    public class AppListAdapter extends  BaseAdapter {
25        private List<ResolveInfo> resolveInfoList;
26
27        public AppListAdapter(List<ResolveInfo> appInfo) {
28            this.resolveInfoList = appInfo;
29        }
30
31        @Override
32        public int getCount() {
33            return resolveInfoList.size();
34        }
35
36        @Override
37        public Object getItem(int position) {
38            return resolveInfoList.get(position);
39        }
40
41        @Override
42        public long getItemId(int position) {
43            return position;
44        }
45
46        @Override
47        public View getView(int position, View convertView, ViewGroup parent) {
48            LayoutInflater layoutInflater = getLayoutInflater();
49            convertView = layoutInflater.inflate(R.layout.item_app_list, null);
50            ImageView iv = convertView.findViewById(R.id.app_icon_iv);
51            TextView tv = convertView.findViewById(R.id.app_name_tv);
52            ResolveInfo resolveInfo = resolveInfoList.get(position);
53            tv.setText(resolveInfo.activityInfo.loadLabel(getPackageManager()));
54            iv.setImageDrawable(resolveInfo.activityInfo.loadIcon(getPackageManager()));
55
56            // 在这里给每一个条目设置点击事件
57            convertView.setOnClickListener((v) -> {
58                String packageName = resolveInfo.activityInfo.packageName;
59                String className = resolveInfo.activityInfo.name;
60                ComponentName componentName = new ComponentName(packageName, className);
61                Intent intent = new Intent();
62                intent.setComponent(componentName);
63                startActivity(intent);
64            });
65            return convertView;
66        }
67    }
68
69
70    // 最基础的数据展示,ImageView是固定的
71    public class AppListAdapterBase extends BaseAdapter {
72
73        @Override
74        public int getCount() {
75            return appNameList.size();
76        }
77
78        @Override
79        public Object getItem(int position) {
80            return appNameList.get(position);
81        }
82
83        @Override
84        public long getItemId(int position) {
85            return position;
86        }
87
88        @Override
89        public View getView(int position, View convertView, ViewGroup parent) {
90            LayoutInflater layoutInflater = getLayoutInflater();
91            convertView = layoutInflater.inflate(R.layout.item_app_list, null);
92            ImageView iv = convertView.findViewById(R.id.app_icon_iv);
93            TextView tv = convertView.findViewById(R.id.app_name_tv);
94            tv.setText(appNameList.get(position));
95            return convertView;
96        }
97    }
98}

点击事件与长按事件

 1public class AppListActivity extends AppCompatActivity {
 2    
 3    @Override
 4    protected void onCreate(Bundle savedInstanceState) {
 5        super.onCreate(savedInstanceState);
 6        setContentView(R.layout.activity_app_list);
 7
 8        ListView listView = findViewById(R.id.app_lv);
 9        appNameList = Arrays.asList("QQ", "微信", "慕课网", "牛客");
10
11        List<ResolveInfo> resolveInfoList = getAppInfo();
12        listView.setAdapter(new AppListAdapter(resolveInfoList));
13
14        // 点击事件写法二
15        listView.setOnItemClickListener((parent, view, position, id) -> {
16            ResolveInfo resolveInfo = resolveInfoList.get(position);
17            String packageName = resolveInfo.activityInfo.packageName;
18            String className = resolveInfo.activityInfo.name;
19            ComponentName componentName = new ComponentName(packageName, className);
20            Intent intent = new Intent();
21            intent.setComponent(componentName);
22            startActivity(intent);
23        });
24
25        // 长按的事件
26        listView.setOnItemLongClickListener((parent, view, position, id) -> {
27            AlertDialog.Builder builder = new AlertDialog.Builder(this);
28            builder.setTitle("提示");
29            builder.setMessage("确定删除吗?");
30            builder.setPositiveButton("确定", (dialog, which) -> {
31                // 移除此条目
32                resolveInfoList.remove(position);
33                listView.setAdapter(new AppListAdapter(resolveInfoList));
34            });
35            builder.setNegativeButton("取消", null);
36            builder.show();
37            return false;
38        });
39    }
40
41    // 获取所有的应用信息
42    private List<ResolveInfo> getAppInfo(){
43        Intent intent = new Intent(Intent.ACTION_MAIN, null);
44        intent.addCategory(Intent.CATEGORY_LAUNCHER);
45        return getPackageManager().queryIntentActivities(intent, 0);
46    }
47
48    public class AppListAdapter extends  BaseAdapter {
49        private List<ResolveInfo> resolveInfoList;
50
51        public AppListAdapter(List<ResolveInfo> appInfo) {
52            this.resolveInfoList = appInfo;
53        }
54
55        @Override
56        public int getCount() {
57            return resolveInfoList.size();
58        }
59
60        @Override
61        public Object getItem(int position) {
62            return resolveInfoList.get(position);
63        }
64
65        @Override
66        public long getItemId(int position) {
67            return position;
68        }
69
70        @Override
71        public View getView(int position, View convertView, ViewGroup parent) {
72            LayoutInflater layoutInflater = getLayoutInflater();
73            convertView = layoutInflater.inflate(R.layout.item_app_list, null);
74            ImageView iv = convertView.findViewById(R.id.app_icon_iv);
75            TextView tv = convertView.findViewById(R.id.app_name_tv);
76            ResolveInfo resolveInfo = resolveInfoList.get(position);
77            tv.setText(resolveInfo.activityInfo.loadLabel(getPackageManager()));
78            iv.setImageDrawable(resolveInfo.activityInfo.loadIcon(getPackageManager()));
79            // 点击事件写法一
80            convertView.setOnClickListener((v) -> {
81                String packageName = resolveInfo.activityInfo.packageName;
82                String className = resolveInfo.activityInfo.name;
83                ComponentName componentName = new ComponentName(packageName, className);
84                Intent intent = new Intent();
85                intent.setComponent(componentName);
86                startActivity(intent);
87            });
88            return convertView;
89        }
90    }
91}

设置HeaderView与FooterView

header_app_list.xml

 1<?xml version="1.0" encoding="utf-8"?>
 2<LinearLayout
 3    xmlns:android="http://schemas.android.com/apk/res/android"
 4    android:layout_width="match_parent"
 5    android:layout_height="match_parent"
 6    android:orientation="vertical">
 7
 8    <TextView
 9        android:gravity="center"
10        android:textSize="20sp"
11        android:text="This is listView's header."
12        android:id="@+id/header_app_list_iv"
13        android:background="@color/colorAccent"
14        android:layout_width="match_parent"
15        android:layout_height="80dp"/>
16
17</LinearLayout>

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

 1public class AppListActivity extends AppCompatActivity {
 2
 3    private List<String> appNameList;
 4
 5    @Override
 6    protected void onCreate(Bundle savedInstanceState) {
 7        super.onCreate(savedInstanceState);
 8        setContentView(R.layout.activity_app_list);
 9
10        ListView listView = findViewById(R.id.app_lv);
11        appNameList = Arrays.asList("QQ", "微信", "慕课网", "牛客", "招商银行");
12
13        // 增加头Header
14        listView.addHeaderView(getLayoutInflater().inflate(R.layout.header_app_list, null));
15
16        List<ResolveInfo> resolveInfoList = getAppInfo();
17        listView.setAdapter(new AppListAdapter(resolveInfoList));
18
19        // 点击事件写法二
20        listView.setOnItemClickListener((parent, view, position, id) -> {
21            ResolveInfo resolveInfo = resolveInfoList.get(position);
22            String packageName = resolveInfo.activityInfo.packageName;
23            String className = resolveInfo.activityInfo.name;
24            ComponentName componentName = new ComponentName(packageName, className);
25            Intent intent = new Intent();
26            intent.setComponent(componentName);
27            startActivity(intent);
28        });
29
30        // 长按的事件
31        listView.setOnItemLongClickListener((parent, view, position, id) -> {
32            AlertDialog.Builder builder = new AlertDialog.Builder(this);
33            builder.setTitle("提示");
34            builder.setMessage("确定删除吗?");
35            builder.setPositiveButton("确定", (dialog, which) -> {
36                resolveInfoList.remove(position);
37                listView.setAdapter(new AppListAdapter(resolveInfoList));
38            });
39            builder.setNegativeButton("取消", null);
40            builder.show();
41            return false;
42        });
43    }
44
45    // 获取所有的应用信息
46    private List<ResolveInfo> getAppInfo(){
47        Intent intent = new Intent(Intent.ACTION_MAIN, null);
48        intent.addCategory(Intent.CATEGORY_LAUNCHER);
49        return getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_ALL);
50    }
51
52    public class AppListAdapter extends  BaseAdapter {
53        private List<ResolveInfo> resolveInfoList;
54
55        public AppListAdapter(List<ResolveInfo> appInfo) {
56            this.resolveInfoList = appInfo;
57        }
58
59        @Override
60        public int getCount() {
61            return resolveInfoList.size();
62        }
63
64        @Override
65        public Object getItem(int position) {
66            return resolveInfoList.get(position);
67        }
68
69        @Override
70        public long getItemId(int position) {
71            return position;
72        }
73
74        @Override
75        public View getView(int position, View convertView, ViewGroup parent) {
76            LayoutInflater layoutInflater = getLayoutInflater();
77            convertView = layoutInflater.inflate(R.layout.item_app_list, null);
78            ImageView iv = convertView.findViewById(R.id.app_icon_iv);
79            TextView tv = convertView.findViewById(R.id.app_name_tv);
80            ResolveInfo resolveInfo = resolveInfoList.get(position);
81            tv.setText(resolveInfo.activityInfo.loadLabel(getPackageManager()));
82            iv.setImageDrawable(resolveInfo.activityInfo.loadIcon(getPackageManager()));
83            return convertView;
84        }
85    }
86}

ListView的优化

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

 1public class AppListActivity extends AppCompatActivity {
 2
 3    private List<String> appNameList;
 4
 5    @Override
 6    protected void onCreate(Bundle savedInstanceState) {
 7        super.onCreate(savedInstanceState);
 8        setContentView(R.layout.activity_app_list);
 9
10        ListView listView = findViewById(R.id.app_lv);
11        appNameList = Arrays.asList("QQ", "微信", "慕课网", "牛客", "招商银行", "支付宝");
12
13        // 增加头Header
14        listView.addHeaderView(getLayoutInflater().inflate(R.layout.header_app_list, null));
15
16        List<ResolveInfo> resolveInfoList = getAppInfo();
17        listView.setAdapter(new AppListAdapter(resolveInfoList));
18    }
19
20    // 获取所有的应用信息
21    private List<ResolveInfo> getAppInfo(){
22        Intent intent = new Intent(Intent.ACTION_MAIN, null);
23        intent.addCategory(Intent.CATEGORY_LAUNCHER);
24        return getPackageManager().queryIntentActivities(intent, 0);
25    }
26    
27    public class AppListAdapter extends  BaseAdapter {
28        private List<ResolveInfo> resolveInfoList;
29
30        public AppListAdapter(List<ResolveInfo> appInfo) {
31            this.resolveInfoList = appInfo;
32        }
33
34        @Override
35        public int getCount() {
36            return resolveInfoList.size();
37        }
38
39        @Override
40        public Object getItem(int position) {
41            return resolveInfoList.get(position);
42        }
43
44        @Override
45        public long getItemId(int position) {
46            return position;
47        }
48
49        @Override
50        public View getView(int position, View convertView, ViewGroup parent) {
51            ViewHolder viewHolder;
52            if(convertView == null){
53                LayoutInflater layoutInflater = getLayoutInflater();
54                convertView = layoutInflater.inflate(R.layout.item_app_list, null);
55                viewHolder = new ViewHolder();
56                viewHolder.imageView = convertView.findViewById(R.id.app_icon_iv);
57                viewHolder.textView = convertView.findViewById(R.id.app_name_tv);
58                convertView.setTag(viewHolder);
59            }else{
60                viewHolder = (ViewHolder) convertView.getTag();
61            }
62            ResolveInfo resolveInfo = resolveInfoList.get(position);
63            viewHolder.textView.setText(resolveInfo.activityInfo.loadLabel(getPackageManager()));
64            viewHolder.imageView.setImageDrawable(resolveInfo.activityInfo.loadIcon(getPackageManager()));
65            return convertView;
66        }
67    }
68
69    // ViewHolder
70    private static class ViewHolder {
71        public ImageView imageView;
72        public TextView textView;
73    }
74}

条目布局分类加载

item_left_chat.xml

 1<?xml version="1.0" encoding="utf-8"?>
 2<androidx.constraintlayout.widget.ConstraintLayout
 3    xmlns:android="http://schemas.android.com/apk/res/android"
 4    xmlns:app="http://schemas.android.com/apk/res-auto"
 5    xmlns:tools="http://schemas.android.com/tools"
 6    android:layout_width="match_parent"
 7    android:layout_height="match_parent">
 8
 9    <TextView
10        android:id="@+id/time_tv"
11        android:layout_width="wrap_content"
12        android:layout_height="wrap_content"
13        android:text="21:52"
14        app:layout_constraintEnd_toEndOf="parent"
15        app:layout_constraintStart_toStartOf="parent"
16        tools:ignore="MissingConstraints" />
17
18    <ImageView
19        android:id="@+id/icon_iv"
20        android:src="@mipmap/ic_launcher"
21        android:layout_width="wrap_content"
22        android:layout_height="wrap_content"
23        app:layout_constraintTop_toBottomOf="@+id/time_tv"
24        tools:ignore="MissingConstraints"
25        />
26
27    <TextView
28        android:id="@+id/name_tv"
29        android:layout_width="wrap_content"
30        android:layout_height="wrap_content"
31        android:text="Tim"
32        app:layout_constraintEnd_toEndOf="@+id/icon_iv"
33        app:layout_constraintStart_toStartOf="@+id/icon_iv"
34        app:layout_constraintTop_toBottomOf="@+id/icon_iv" />
35
36    <TextView
37        android:id="@+id/content_tv"
38        android:layout_width="wrap_content"
39        android:layout_height="wrap_content"
40        android:text="Hello, how are you?"
41        android:paddingStart="10sp"
42        app:layout_constraintBottom_toBottomOf="@+id/icon_iv"
43        app:layout_constraintStart_toEndOf="@+id/icon_iv"
44        app:layout_constraintTop_toTopOf="@+id/icon_iv"
45        tools:ignore="MissingConstraints" />
46</androidx.constraintlayout.widget.ConstraintLayout>

item_right_chat.xml

 1<?xml version="1.0" encoding="utf-8"?>
 2<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3    xmlns:app="http://schemas.android.com/apk/res-auto"
 4    xmlns:tools="http://schemas.android.com/tools"
 5    android:layout_width="match_parent"
 6    android:layout_height="match_parent">
 7
 8    <TextView
 9        android:id="@+id/time_tv"
10        android:layout_width="wrap_content"
11        android:layout_height="wrap_content"
12        android:text="21:52"
13        app:layout_constraintEnd_toEndOf="parent"
14        app:layout_constraintStart_toStartOf="parent"
15        tools:ignore="MissingConstraints" />
16
17    <TextView
18        android:id="@+id/name_tv"
19        android:layout_width="wrap_content"
20        android:layout_height="wrap_content"
21        android:text="Tim"
22        app:layout_constraintEnd_toEndOf="@+id/icon_iv"
23        app:layout_constraintStart_toStartOf="@+id/icon_iv"
24        app:layout_constraintTop_toBottomOf="@+id/icon_iv" />
25
26    <TextView
27        android:id="@+id/content_tv"
28        android:layout_width="wrap_content"
29        android:layout_height="wrap_content"
30        android:text="Hello, how are you?"
31        android:paddingEnd="10sp"
32        app:layout_constraintBottom_toBottomOf="@+id/icon_iv"
33        app:layout_constraintEnd_toStartOf="@+id/icon_iv"
34        app:layout_constraintTop_toTopOf="@+id/icon_iv"
35        tools:ignore="MissingConstraints" />
36
37    <ImageView
38        android:id="@+id/icon_iv"
39        android:layout_width="wrap_content"
40        android:layout_height="wrap_content"
41        android:src="@mipmap/ic_launcher"
42        app:layout_constraintEnd_toEndOf="parent"
43        app:layout_constraintTop_toBottomOf="@+id/time_tv"
44        tools:ignore="MissingConstraints" />
45</androidx.constraintlayout.widget.ConstraintLayout>

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

  1public class MainActivity extends AppCompatActivity {
  2
  3    @Override
  4    protected void onCreate(Bundle savedInstanceState) {
  5        super.onCreate(savedInstanceState);
  6        setContentView(R.layout.activity_main);
  7        ListView listView = findViewById(R.id.main_lv);
  8        List<ChatMessage> chatMessages = Arrays.asList(
  9                new ChatMessage(1, 2, "Tim", "08:20", "I'm Tim.", true),
 10                new ChatMessage(1, 2, "Tim", "08:25", "Jone, how are you?", true),
 11                new ChatMessage(2, 1, "Jone", "08:30", "I'm fine, thinks", false),
 12                new ChatMessage(1, 2, "Tim", "08:31", "No thinks.", true),
 13                new ChatMessage(2, 1, "Jone", "08:32", "What can I do for you ?", false),
 14                new ChatMessage(1, 2, "Tim", "08:59", "Please give me some money.", true)
 15        );
 16        listView.setAdapter(new ChatMessageAdapter(chatMessages, MainActivity.this));
 17
 18    }
 19
 20    static class ChatMessage {
 21        public int mId;
 22        public int mFriendId;
 23        public String mName;
 24        public String mDate;
 25        public String mContent;
 26        public boolean mIsComeMessage;
 27
 28        public ChatMessage(int mId, int mFriendId,
 29                           String mName, String mDate,
 30                           String mContent, boolean mIsComeMessage) {
 31            this.mId = mId;
 32            this.mFriendId = mFriendId;
 33            this.mName = mName;
 34            this.mDate = mDate;
 35            this.mContent = mContent;
 36            this.mIsComeMessage = mIsComeMessage;
 37        }
 38    }
 39
 40    static class ChatMessageAdapter extends BaseAdapter {
 41        List<ChatMessage> chatMessages;
 42        Context context;
 43
 44        interface IMessageViewType {
 45            int COM_MESSAGE = 1;
 46            int TO_MESSAGE = 2;
 47        }
 48
 49        public ChatMessageAdapter(List<ChatMessage> chatMessages, Context context) {
 50            this.chatMessages = chatMessages;
 51            this.context = context;
 52        }
 53
 54        @Override
 55        public int getCount() {
 56            return chatMessages.size();
 57        }
 58
 59        @Override
 60        public Object getItem(int position) {
 61            return chatMessages.get(position);
 62        }
 63
 64        @Override
 65        public long getItemId(int position) {
 66            return position;
 67        }
 68
 69        @Override
 70        public View getView(int position, View convertView, ViewGroup parent) {
 71            LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
 72            ChatMessage chatMessage = chatMessages.get(position);
 73            if(convertView == null){
 74                if(chatMessage.mIsComeMessage){
 75                    convertView = layoutInflater.inflate(R.layout.item_left_chat, null);
 76                }else{
 77                    convertView = layoutInflater.inflate(R.layout.item_right_chat, null);
 78                }
 79                TextView timeTv = convertView.findViewById(R.id.time_tv);
 80                ImageView iconIv = convertView.findViewById(R.id.icon_iv);
 81                TextView nameTv = convertView.findViewById(R.id.name_tv);
 82                TextView contentTv = convertView.findViewById(R.id.content_tv);
 83                timeTv.setText(chatMessage.mDate);
 84                nameTv.setText(chatMessage.mName);
 85                contentTv.setText(chatMessage.mContent);
 86            }
 87            return convertView;
 88        }
 89
 90        @Override
 91        public int getItemViewType(int position) {
 92            ChatMessage chatMessage = chatMessages.get(position);
 93            return chatMessage.mIsComeMessage ? 
 94                IMessageViewType.COM_MESSAGE : IMessageViewType.TO_MESSAGE;
 95        }
 96
 97        @Override
 98        public int getViewTypeCount() {
 99            return 2; // IMessageViewType 两种类型
100        }
101    }
102}

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的依赖

1implementation 'androidx.cardview:cardview:1.0.0'

一个VardView的小Demo:

 1<?xml version="1.0" encoding="utf-8"?>
 2<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3    xmlns:tools="http://schemas.android.com/tools"
 4    android:layout_width="match_parent"
 5    android:layout_height="match_parent"
 6    xmlns:app="http://schemas.android.com/apk/res-auto"
 7    tools:context=".MainActivity">
 8
 9    <!--
10    cardBackgroundColor:设置背景色
11    cardCornerRadius:设置圆角
12    cardElevation:设置阴影
13    contentPadding:设置内部padding
14    -->
15    <androidx.cardview.widget.CardView
16        android:layout_gravity="center"
17        app:cardBackgroundColor="@color/colorAccent"
18        app:cardCornerRadius="10dp"
19        app:cardElevation="5dp"
20        app:contentPadding="10dp"
21        android:layout_width="wrap_content"
22        android:layout_height="wrap_content">
23
24        <TextView
25            android:layout_width="200dp"
26            android:layout_height="50dp"
27            android:text="Hello World!"
28            android:gravity="center"
29            android:layout_gravity="center"
30            />
31    </androidx.cardview.widget.CardView>
32</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<?xml version="1.0" encoding="utf-8"?>
 2<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3    android:layout_width="match_parent"
 4    android:layout_height="wrap_content"
 5    xmlns:tools="http://schemas.android.com/tools"
 6    xmlns:app="http://schemas.android.com/apk/res-auto">
 7
 8    <androidx.cardview.widget.CardView
 9        app:cardCornerRadius="8dp"
10        app:cardElevation="5dp"
11        app:cardUseCompatPadding="false"
12        android:layout_marginLeft="16dp"
13        android:layout_marginRight="16dp"
14        android:layout_marginTop="8dp"
15        android:layout_marginBottom="8dp"
16        android:layout_width="match_parent"
17        android:layout_height="wrap_content">
18        <LinearLayout
19            android:orientation="vertical"
20            android:layout_width="match_parent"
21            android:layout_height="wrap_content">
22            <ImageView
23                android:id="@+id/item_icon_iv"
24                android:scaleType="centerCrop"
25                tools:src="@drawable/img01"
26                android:layout_width="match_parent"
27                android:layout_height="wrap_content"/>
28
29            <TextView
30                android:id="@+id/item_title_tv"
31                android:layout_margin="8dp"
32                android:textColor="#000000"
33                android:textSize="16sp"
34                android:textStyle="bold"
35                tools:text="一起来学习Android技术"
36                android:layout_width="match_parent"
37                android:layout_height="wrap_content"/>
38
39            <TextView
40                android:id="@+id/item_content_tv"
41                android:layout_margin="8dp"
42                android:textColor="#000000"
43                android:layout_marginLeft="8dp"
44                android:layout_marginRight="8dp"
45                android:layout_marginBottom="8dp"
46                tools:text="一起来学习Android技术, 一起来学习Android技术。"
47                android:layout_width="match_parent"
48                android:layout_height="wrap_content"/>
49        </LinearLayout>
50    </androidx.cardview.widget.CardView>
51</FrameLayout>

main_activity.xml

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

MainActivity.java

 1public class MainActivity extends AppCompatActivity {
 2
 3    @Override
 4    protected void onCreate(Bundle savedInstanceState) {
 5        super.onCreate(savedInstanceState);
 6        this.setTitle("CardView测试");
 7        setContentView(R.layout.activity_main);
 8        ListView msgListView = findViewById(R.id.id_listview_msg_list);
 9
10        List<Msg> messageList = Arrays.asList(
11                new Msg(1, R.drawable.img01, "如何才能不错过人工智能的时代?", "下一个时代就是机器学习的时代,与你一起预见未来!"),
12                new Msg(2, R.drawable.img02, "关于你的面试、实习心路历程", "奖品丰富,更设有参与奖,随机抽取5名幸运用户,获得付费面试课程中的任意一门!"),
13                new Msg(3, R.drawable.img03, "狗粮不是你想吃,就能吃的!", "你的朋友圈开始了吗?一半秀恩爱,一半扮感伤!不怕,陪你坚强地走下去!"),
14                new Msg(4, R.drawable.img04, "前端跳槽面试那些事儿~", "工作有几年了,项目偏简单有点拿不出手怎么办?目前还没毕业,正在自学前端,请问可以找到一份前端工作吗,我该怎么办?"),
15                new Msg(5, R.drawable.img05, "图解程序员怎么过七夕?", "图解程序员怎么过七夕,哈哈哈哈,活该单身25年!")
16        );
17
18        msgListView.setAdapter(new MoocAdapter(messageList));
19    }
20
21    class MoocAdapter extends BaseAdapter {
22        private List<Msg> msgList;
23
24        public MoocAdapter(List<Msg> msgList) {
25            this.msgList = msgList;
26        }
27
28        @Override
29        public int getCount() {
30            return msgList.size();
31        }
32
33        @Override
34        public Object getItem(int position) {
35            return msgList.get(position);
36        }
37
38        @Override
39        public long getItemId(int position) {
40            return position;
41        }
42
43        @Override
44        public View getView(int position, View convertView, ViewGroup parent) {
45            ViewHolder viewHolder;
46            if(convertView == null){
47                convertView = View.inflate(MainActivity.this, R.layout.item_msg, null);
48                //convertView = getLayoutInflater().inflate(R.layout.item_msg, null);
49                viewHolder = new ViewHolder();
50                viewHolder.iconImageView = convertView.findViewById(R.id.item_icon_iv);
51                viewHolder.titleTextView = convertView.findViewById(R.id.item_title_tv);
52                viewHolder.contentTextView = convertView.findViewById(R.id.item_content_tv);
53                convertView.setTag(viewHolder);
54            }else{
55                viewHolder = (ViewHolder) convertView.getTag();
56            }
57            Msg msg = msgList.get(position);
58            viewHolder.contentTextView.setText(msg.getContent());
59            viewHolder.titleTextView.setText(msg.getTitle());
60            viewHolder.iconImageView.setImageResource(msg.getImgResId());
61
62            return convertView;
63        }
64    }
65
66    static class ViewHolder {
67        ImageView iconImageView;
68        TextView titleTextView;
69        TextView contentTextView;
70    }
71}
72
73@Data
74@AllArgsConstructor
75@NoArgsConstructor
76class Msg {
77    private int id;
78    private int imgResId;
79    private String title;
80    private String content;
81}

看看最终的展示效果:

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

使用tools工具预览

 1<!-- 引入tools命令空间 -->
 2xmlns:tools="http://schemas.android.com/tools"
 3
 4<TextView
 5      android:id="@+id/item_title_tv"
 6      android:layout_margin="8dp"
 7      android:textColor="#000000"
 8      android:textSize="16sp"
 9      android:textStyle="bold"
10      tools:text="一起来学习Android技术"
11      android:layout_width="match_parent"
12      android:layout_height="wrap_content"/>

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

Lombok在Android中的使用

引入lombok的依赖:

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

安装Lombok的插件:

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

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

1lombok.anyConstructor.suppressConstructorProperties=true

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

编译时编码错误解决方案

 1android {
 2    compileSdkVersion ...
 3    buildToolsVersion "..."
 4    
 5    // 指定编码为UTF-8
 6    compileOptions {
 7        encoding "UTF-8"
 8    }
 9    ...
10}

Android屏幕适配

这个专门放置一篇博客!