2.android布局及UI控件

布局

0、约束布局

1、线性布局(LinearLayout)

常见属性

以下属性为常见属性,除了最后一个。

1、组件名称:+id/

android:+id/name:代表声明一个新的元素
android:id/name:代表直接使用以及存在的元素

2、组件高宽:

android:layout_width
android:layout_height
1、属性值:
wrap_content 代表实际内容尺寸
match_parent 与父级元素尺寸
fill_parent 填充整个父级尺寸

2、属性值单位:
尺寸单位:dp

3、组件背景:backgroud

adnroid:backgroud
属性值:颜色代码、图片

4、同级元素或者与父控件距离:layout_margin*

android:layout_margin*

5、子元素之间距离:layout_padding*

android:layout_padding*

6、子元素排列方向:orientation

在父容器中设置android:orientation
属性值:
1、垂直:vertical
2、水行:horizontal
3、默认水平方向,左边排列

7、子元素对齐方式:grivity

在子元素中设置:android:grivity
比如在底部,垂直居中、右上角等,

8、子元素空间占比权重:layout_weight

子元素中设置:layout_weight
1、比如两个元素的高宽均为0dp,权重都为1,表示各种权重为1,各占50%的空间。
2、如果一个元素的宽为50dp,权重各为1,代表1元素先占50dp,剩余均分

9 一行显示:sigleLine

true即为一行显示

2、相对布局(RelativeLayout)

以下属性为常见属性。

1、在某元素左侧

android:layout_toLeftOf

2、在某元素右侧

android:layout_toRightOf

3、跟某元素的底部对齐

android:layout_alignBottom

4、在父元素底部对齐

android:layout_alignParentBottom

5、在谁的下面

android:layout_layout_below

其他控件

1. TextView

1、主要内容如下

1.文字大小
2.超出省略号
3.文字+icon
4.中划线-下划线
5.跑马灯

在布局中声明元素后,通过findViewById(R.id.元素id)

现在实现个按钮点击后,实现如上TextView文字效果。

2.1、第一步:

在布局文件中,声明一个button,id是btn_textView,然后在acitivity中创建点击事件。

//声明button变量
private Button mbtnTextView;
@override
protected void onCreate(Bunle saveInstanceSate){
    super.onCreate(saveInstanceState);
    setContentView(R.layout.activity_main);
    
    mbtnTextView = (Botton)findViewById(R.id.btn_textView)
    //单击事件
    mbtnTextView。setOnClikeListener(new View.OnClikListener){
        @override
        public void onClik(View v){
            //跳转到TextView
        }
    };
    )
}

2.2、第二步:

创建一个新的TextView activity作为独立界面,androidMainFest.xml注册。并继续在button单击事件中添加如下代码:

Intent intent = new Intent(当前activity类名.this,TextViewactivity.class);
startActivity(intent)

2.3、第三步:

修改TextViewTextView.xml布局,增加一个TextView组件。常见属性如下:

android:text 是文本

android:textColor 颜色

android:textSize 字体大小,单位sp

android:ellipsize="end" 超出尺寸文本用...代替。

android:drawableRight="@drawable/icon_xx" 给文本右侧设置一个图片

android:drawablePadding 文字与图片距离

3、中划线、下划线需要在代码中设置:

tv = findViewById(R.id.tv4)
tv.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG);
//去锯齿
tv.getPaint().setAntAlias(true)

4、跑马灯效果:

android:singleLine=true
android:ellipsize=marquee
android:marqueeRepeatLimit="marquee_foever"
android:focusable=true 焦点才能一直有效果
android:focusableIntochModel=true

2. button

1、圆角button

在drawable创建一个drawable resouce file 文件bg_btn,root element选择share

android:shape 图形属性
android:color 填充颜色
<corners android:radius='5dp'/> 圆角

然后将button的backgroud设置为"@drawable/bg_btn"

2、描边button

在drawable创建一个drawable resouce file 文件bg_bt2

<stroke android:width="1dp"
android:color="#ffccc"/>

然后将button的backgroud换掉即可.

3、button按压效果

<item android:state_pressed="true">
    <share>
        <solid android:color="#ff9900"/>
        <corners android:radius="5dp"/>
    </share>
</item>


<item android:state_pressed="false">
    <share>
       正常的效果
    </share>
</item>

4、按钮点击弹窗

在单击事件里:

Toast.makeText(aitive.this,'文本',Toast.LENGTH_SHORT)

3.EditText

1、常见属性

默认EditText是只有下边框的.如果要自定义,需要自建drawable下建立 drawable resouce file文件.
密码框,其他属性与一般ui相同.

<EditText
android:hint="密码"
android:inputType="textPassword"
android:maxLine=1
android:drawableLeft="图标"
andrlid:drawablePadding="5dp"
/>

如果是数字,只需inputType设置为number.

2、 文本变化监听事件

EditText.andTextChangedListener(new View.OnClikListener){
        @override
        public beforeTextChange(){
            //改变之前
        }
        @override
        public onTextChange(CharSequence s,int start,int before,int count){
            //改变之后
            //CharSequence是当前输入的文字
            //日志打印
            Log.d("input",s.toString())
        }
        @override
        public afterTextChange(){
            //改变之后
        }
        
    };
    )

4.单选:RadioButton

1、常见属性

<RadioGroup>
    <RadioButton
    android:id
    android:layout_width
    android:layout_height
    android:text='男'
    android:check="true" //默认选中,必须有id
    android:orientation="" //方向
    android:button="@null" //去掉单选小圆圈
    android:backgroupd="@drawable/select" //自定义样式
    />
</RadioGroup>

2、select:自定义样式

<selector>
<share android:state_checked="true">
       选中效果
</share>
</item>

<item android:state_checked="false">
    <share>
       正常的效果
    </share>
</selector>

3、监听事件

m=findViewById(R.id.rg1)
m.setOnclickChangeListener(new RadioGroup.OncheckkedChangeListener(){
    public void onCheckedChange(RadioGroup group,@IDres int checkedId){
        //选中的redio
        RadioButton = group.findViewById(checkedId)
        Toast.make(本activity.this,"",Tost.Lentgh_short).show();
    }
});

5.UI自定义样式

<shape>
<solid android:color="背景色"/>

<!--边框设计-->
<stroke  android:width="边框粗细" android:color="边框颜色"/>

<!--圆角设计-->
<corners 
android:bottomLeftRadius="10sp图形左下角圆角"
android:topLeftRadius="10sp"
android:bottomRightRadius="10sp"
android:topRightRadius=""
/>

<!--颜色渐变-->
<gradient
android:startColor="开始颜色"
android:endColor="结速颜色"
android:centerColor="过度颜色"
android:angle="渐变方向"
android:type="linear渐变方式"

/>

</shape>

渐变方向:

0:从左向右渐变

90:从下向上渐变

180:从右向左渐变

270:从上向下渐变

6.复选框

7.ImageView

ImageView是图片组件.文件通常存储在drawable开头的目录下.不同分辨率的图片存储在不同以drawable开头的目录下.创建一个临时的drawable-xhdpi,复制图片进去.初始的图片设定有android:src="@drawable/图片名称".宽度及高度使用wrap_content,表明根据实际情况来设定.

代码修改图片:

imageView = (ImageView)findViewById(R.id.image_name)
imageView.setImageRrsource(R.drawable.image_name)

8.ProgressBar

原型的加载进度条.加载万,通过控件的可见属性设置即可.可见属性有三种可选:

android:visibility=visible/invisible/gone

visible是可见,默认值;invisible是不可见,但仍然占据位置;gone是不可见,不占用屏幕空间.可以通过如下方法设定:

a.setVisiblebility(View.VISIBLE\View.INVISIBLE\View.GONE)

9.AlertDialog

                AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity4.this);
                dialog.setTitle("对话框");
                dialog.setMessage("要回传的数据:你好");
                //释放点击对话框外部的屏幕关闭对话框
                dialog.setCancelable(false);
                //确认按钮的逻辑
                dialog.setPositiveButton("确认", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast.makeText(MainActivity4.this,"确认1",Toast.LENGTH_LONG).show();
                    }
                });
                //取消按钮的逻辑
                dialog.setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Toast.makeText(MainActivity4.this,"取消",Toast.LENGTH_LONG).show();
                    }
                });
                //显示对话框
                dialog.show();
                //finish()
                

另外需要注意的是,dialog不是阻塞运行的,dialog.show()后面的代码会继续运行.

11.progressDialog加载对话框

是加载对话框,数据完成后,需要手动dialog.dismiss()关闭,不然会一直存在.

12.自定义控件

UI空间都是继承子View,布局都是继承ViewGroup.

view是一种基本空间,在屏幕上划分一块矩形区域,能响应矩形区域内的各种事件.Viewgrup是一种特殊控件,包含不少view和子ViewGROUP.

创建一个自定义title

  • 自定义UI及配置
    自定义一个通用的top bar.就像苹果的title,包括back和exit按钮.

首先创建一个布局,起名为title. 里面为相对布局,增加两个按钮,一个是back一个是exit.back靠top左,exit靠top 右.

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <Button
        android:id="@+id/button_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:textSize="20sp"
        android:layout_alignParentLeft="true"
        android:text="back" />

    <Button
        android:id="@+id/button_exit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_margin="5dp"
        android:layout_alignParentRight="true"
        android:text="exit"
        android:textSize="20sp" />
</RelativeLayout>

然后在其他action的布局文件中,增加如下配置:

<include layout="@layout/title"></include>

同时在oncreate()代码中隐藏action自带的top bar:

        ActionBar bar=getSupportActionBar();
        bar.hide();
  • 给布局增加事件

自此,自定义title组件可以看到效果了.但是back和exit的事件需要在每个action写相同的代码,这样很不好.

我们可以通过继承linearlayout,绑定title ui布局文件.

public class TitleLayout extends LinearLayout {
    public TitleLayout(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        LayoutInflater.from(context).inflate(R.layout.title,this);
        Button back = findViewById(R.id.button_back);
        back.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                ((Activity) getContext()).finish();
            }
        });
    }
}

在布局中替换上面的

<include layout="@layout/title"></include>

如下:

<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"
    tools:context=".MainActivity4">
<com.example.myapplication.TitleLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

13.ListView控件

1、简单的listview使用

listview出镜率非常高.本节演示一个只支持文本的listview.具体步骤如下:

1、创建一个ListViewActivity,在布局中增加listView
2、准备listView数据,用list保存
3、创建listView适配器,适配器将会在内部为数据创建子布局
4、listView设置适配器

1、创建ListViewActivity

创建一个ListViewActivity,并在对应的activity_list_view布局文件中增加listView组件.activity_list_view.xml布局:

<?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"
    tools:context=".LlistViewActivity">
    
    <ListView android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:layout_editor_absoluteX="87dp"
        tools:layout_editor_absoluteY="84dp" />
</androidx.constraintlayout.widget.ConstraintLayout>

通过布局可知,listView组件已经填充了整个布局文件.
下面在action里填充数据.

2、初始化数据

用循环生成了array数据,listview无法直接绑定数据,所以要说用ArrayAdapter适配器先处理数据.ListViewActivity.class:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_llist_view);
        //数组填充数据
        ArrayList data= new ArrayList();
        for(int i=0;i<100;i++){
            data.add(i+"x");
        }
        //数据适配器,指定了显示的action,以及listview里面的子布局
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(LlistViewActivity.this, android.R.layout.simple_list_item_1,data);
        ListView lv = findViewById(R.id.list_view);
        lv.setAdapter(adapter);
    }

说明:

ArrayAdapter初始化参数共三个,第一个是当前action,第二个是子布局,第三个是单位.

listView本身其实也是容器,适配器通过指定的子布局进行组装数据,然后listview通过setAdapter(adapter)直接进行装载.
本文使用的内置布局android.R.layout.simple_list_item_1,就是一个简单TextView组件,适配器会根据数据动态的创建TextView用来显示数据.

2、listView显示图片

listView本身是一个容器,里面是子布局.所以要显示图片,我们需要创建一个布局,这个布局没有根容器,然后把子布局放入listView即可.

为了完成这个目标,我们需要做两件事:

1.自定义子布局
2.自定义实现适配器

1、 自定义listView子布局
子布局为右侧为图片,左侧为文字的list. 先用LinearLayout作为根布局,里面放一个图片组件、文本组件。默认横向排列。list_view_child_layout:

<?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">
    <androidx.appcompat.widget.AppCompatImageView
        android:id="@+id/list_view_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    </androidx.appcompat.widget.AppCompatImageView>
    <TextView
        android:id="@+id/list_view_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginLeft="10dp"
        >
    </TextView>
</LinearLayout>

2、 创建list记录类
用来设置list的图片,文本。ResouceBean作为资源bean,存储图片.
ResouceBean.class:

public class ResouceBean {
    private String name;//图片名称
    private int imageId;//图片ID
    public ResouceBean(String name, int imageId){
        this.name=name;
        this.imageId=imageId;
    }
    public String getName(){
        return name;
    }
    public int getImageId(){
        return imageId;
    }
}

2、 创建适配器

自定义适配器,将接受的数据与子布局进行组装。具体的步骤如下:

1、适配器继承自ArrayAdapter
2、初始化方法参数接收子布局ID
3、重写getView,进行动态装置数据,getView每次滚均会执行
4、

ImageTextAdapter.class

public class ImageTextAdapter extends ArrayAdapter<ResouceBean> {
    private int child_layout_id;//子布局
    public ImageTextAdapter(@NonNull Context context, int child_layout_id, @NonNull List<ResouceBean> objects) {
        super(context, child_layout_id, objects);
        this.child_layout_id =child_layout_id;
    }

    //该方法滚动到屏幕就会调用执行
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        ResouceBean bean = getItem(position);
        Log.d("ImageTextAdapter 图片ID:",bean.getImageId()+"");
        //加载子布局,参数1是子布局id,参数2是根布局,参数3是此子布局自身不设置根布局,不然无法添加到listview
        View child_layout = LayoutInflater.from(getContext()).inflate(child_layout_id,parent,false);
        ImageView iv = child_layout.findViewById(R.id.list_view_image);
        TextView text = child_layout.findViewById(R.id.list_view_text);
        iv.setImageResource(bean.getImageId());
        text.setText(bean.getName());
        return child_layout;
    }
}

适配器初始化,会接受子布局的Id.

getView()是滚动到屏幕就会调用执行,所以在里面装配子布局. 首先使用getItem()获取要显示的资源bean,然后使用LayoutInflater获得子布局,并且为里面的组件进行赋值.

其中inflate第三个参数是指不为此布局添加父布局,不然将会导致无法添加到listView中.完成设置后,返回子布局.

3、 绑定listView

在listview的ListViewActivity中绑定数据:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_llist_view);
        //数组填充数据
        List<ResouceBean> data = new ArrayList<ResouceBean>();
        for(int i=0;i<100;i++){
            ResouceBean bean = new ResouceBean("测试"+i,R.drawable.test);
            data.add(bean);
        }
        //数据适配器,指定了显示的action,以及listview里面的子布局
        ImageTextAdapter adapter = new ImageTextAdapter(this,R.layout.list_view_child_layout,data);
        ListView lv = findViewById(R.id.list_view);
        lv.setAdapter(adapter);
    }

到此为止,已完成.

3、提升listview性能,优化适配器

在上面的例子中,listview的适配器的getView()方法,每次滚动都会执行,重写加载布局.当滚动快的时候,性能问题就来了.

在getView()方法中有个convertView参数,是对之前布局进行缓存的,可以重用. 所以每次在增加一个convertView的判断,如果不为空,直接将convertView 赋值给child_layout. 修改代码如下:

ImageTextAdapter.class

public class ImageTextAdapter extends ArrayAdapter<ResouceBean> {
    private int child_layout_id;//子布局
    public ImageTextAdapter(@NonNull Context context, int child_layout_id, @NonNull List<ResouceBean> objects) {
        super(context, child_layout_id, objects);
        this.child_layout_id =child_layout_id;
    }

    //该方法滚动到屏幕就会调用执行
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        ResouceBean bean = getItem(position);
        Log.d("ImageTextAdapter 图片ID:",bean.getImageId()+"");

        #优先加载缓存子布局
        View child_layout;
        if(convertView==null){
            child_layout = LayoutInflater.from(getContext()).inflate(child_layout_id,parent,false);
        }else{
            view=convertView
        }
        
        ImageView iv = child_layout.findViewById(R.id.list_view_image);
        TextView text = child_layout.findViewById(R.id.list_view_text);
        iv.setImageResource(bean.getImageId());
        text.setText(bean.getName());
        return child_layout;
    }
}

虽然子布局进行了优化,但是每次findViewById()都会获取控件实例,我们是不是只用取一次就行了? 其实只要把findViewById()获取的实例存储起来,下次利用就好了.完整的适配器代码如下:

public class ImageTextAdapter extends ArrayAdapter<ResouceBean> {
    private int child_layout_id;//子布局
    public ImageTextAdapter(@NonNull Context context, int child_layout_id, @NonNull List<ResouceBean> objects) {
        super(context, child_layout_id, objects);
        this.child_layout_id =child_layout_id;
    }

    //该方法滚动到屏幕就会调用执行
    @NonNull
    @Override
    public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
        ResouceBean bean = getItem(position);
        Log.d("ImageTextAdapter 图片ID:",bean.getImageId()+"");
        //子布局及布局的组件,convertView缓存了子布局
        View child_layout;
        //内部类,创建对象后存储起来
        RecyclerViewHolder recyclerViewHodler;

        if (convertView==null){
            child_layout = LayoutInflater.from(getContext()).inflate(child_layout_id,parent,false);
            recyclerViewHodler=new RecyclerViewHolder();
            recyclerViewHodler.imageView=child_layout.findViewById(R.id.list_view_image);
            recyclerViewHodler.textView=child_layout.findViewById(R.id.list_view_text);
            child_layout.setTag(recyclerViewHodler);

        }else {
            child_layout=convertView;
        }
        recyclerViewHodler=(RecyclerViewHolder)child_layout.getTag();

        ImageView iv = recyclerViewHodler.imageView;
        TextView text =recyclerViewHodler.textView;
        iv.setImageResource(bean.getImageId());
        text.setText(bean.getName());
        return child_layout;
    }

    class RecyclerViewHolder{
        ImageView imageView;
        TextView textView;

    }

4、listView子布局组件点击事件

listView绑定适配器后,通过通过setOnItemClickListener设置监听事件.

ListViewActivity.class 的oncreate()完整代码:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_llist_view);
        //数组填充数据
        List<ResouceBean> data = new ArrayList<ResouceBean>();
        for(int i=0;i<100;i++){
            ResouceBean bean = new ResouceBean("测试"+i,R.drawable.test);
            data.add(bean);
        }
        //数据适配器,指定了显示的action,以及listview里面的子布局
        ImageTextAdapter adapter = new ImageTextAdapter(this,R.layout.list_view_child_layout,data);
        ListView lv = findViewById(R.id.list_view);
        lv.setAdapter(adapter);

        //事件
        lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                ResouceBean bean=data.get(position);
                Toast.makeText(ListViewActivity.this,bean.getName(),Toast.LENGTH_SHORT).show();
            }
        });
    }

14.滚动控件RecyclerView

1、listview效果及横向滑动

listview只能纵向滚动,不能横向滚动.andorid提供了一个更加大的控件recyclerView.
recylerView是listview增强版本,目前官方推荐使用recylerView.

要实现listview 图形+文字的同样效果,所以直接使用上面例子的子布局和资源.

1、创建RecylcerViewActivity及布局

创建RecylcerViewActivity及布局,布局中包括recylerview控件.

activity_recylcer_view.xml布局代码如下:

<?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"
    tools:context=".RecylcerViewActivity">


    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerViewid"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

RecylcerViewActivity.class先创建,等一下填充代码。

2、 创建适配器
RecylerViewAdapter.class代码:


public class RecylerViewAdapter extends RecyclerView.Adapter<RecylerViewAdapter.InnViewHolder> {

    private List<ResouceBean> bean = new ArrayList<>();
    static class InnViewHolder extends RecyclerView.ViewHolder{
        ImageView imageView;//list item图片
        TextView textView;//list item文字
        public InnViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView=itemView.findViewById(R.id.list_view_image);
            textView=itemView.findViewById(R.id.list_view_text);
        }
    }

    public RecylerViewAdapter(List<ResouceBean> data){
        bean=data;
        Log.d(this.getClass().toString()+"初始化",data.size()+"");
    }


    @Override
    public InnViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view  = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_view_child_layout,parent,false);
        InnViewHolder holder = new InnViewHolder(view);
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull InnViewHolder holder, int position) {
        ResouceBean resb = bean.get(position);
        Log.d(this.getClass().toString(),resb.getName()+">>>>");
        holder.textView.setText(resb.getName());
        holder.imageView.setImageResource(resb.getImageId());

    }

    @Override
    public int getItemCount() {
        return bean.size();
    }
}

解释:
1.RecylerViewAdapter继承自RecyclerView.Adapter<RecylerViewAdapter.InnViewHolder>,其中RecylerViewAdapter.ViewHolder是该类的一个静态内部类.

2.bean存储传过来的图片,文本数据.

3.InnViewHolder是静态内部类,接受子布局,获取子布局控件对象.

4.RecylerViewAdapter构造函数接受传递过来的图片文本数据.

5.重写onCreateViewHolder,获取子布局,并传递给InnViewHolder内部类.

6.重写onBindViewHolder,给子布局控件设置图片及文字.

7.getItemCount获取数据的长度

需要注意的是,R.layout.list_view_child_layout子布局的跟布局一定要设置一个高度,或者选择高度选择wrap_content,不然一个item就会占满屏幕.

3、修改RecylcerViewActivity

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recylcer_view);
        //数组填充数据
        List<ResouceBean> data = new ArrayList<ResouceBean>();
        for(int i=0;i<100;i++){
            ResouceBean bean = new ResouceBean("测试"+i,R.drawable.test);
            data.add(bean);
        }

        RecyclerView recyclerView=findViewById(R.id.recyclerViewid);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        //横向滑动
        //layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        recyclerView.setLayoutManager(layoutManager);

        RecylerViewAdapter adapter = new RecylerViewAdapter(data);
        recyclerView.setAdapter(adapter);
    }

解释:
1.初始化数据,创建RecyclerView组件

2.给recyler布局创建一个布局管理器

3.将数据传递给适配器,与recyler适配

4.横向滑动:layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

RecyclerView布局切换

RecyclerView使用不同的布局管理器,很容易实现不同的展现效果.

        //线性布局
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        //layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        
        //网格布局
        GridLayoutManager grild = new GridLayoutManager(this,2);
        //grild.setOrientation(GridLayoutManager.HORIZONTAL);
        
        //瀑布流布局
        StaggeredGridLayoutManager sgrild= new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);

        recyclerView.setLayoutManager(sgrild);

recylerView事件

recylerView没有直接提供事件,而是子布局控件直接设置监听事件. recylerView的子布局是在adapter绑定子布局及获取控件的,所以直接在获取控件后,直接设置事件.

RecylerViewAdapter.class 修改onCreateViewHolder方法,控件增加事件的注册.


public class RecylerViewAdapter extends RecyclerView.Adapter<RecylerViewAdapter.InnViewHolder> {

    private List<ResouceBean> bean = new ArrayList<>();
    static class InnViewHolder extends RecyclerView.ViewHolder{
        ImageView imageView;//list item图片
        TextView textView;//list item文字
        public InnViewHolder(@NonNull View itemView) {
            super(itemView);
            imageView=itemView.findViewById(R.id.list_view_image);
            textView=itemView.findViewById(R.id.list_view_text);
        }
    }

    public RecylerViewAdapter(List<ResouceBean> data){
        bean=data;
        Log.d(this.getClass().toString()+"初始化",data.size()+"");
    }
    
    @Override
    public InnViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view  = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_view_child_layout,parent,false);
        InnViewHolder holder = new InnViewHolder(view);

        //item点击事件
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Toast.makeText(v.getContext(),position+"----",Toast.LENGTH_SHORT).show();
            }
        });

        //子布局控件事件
        holder.imageView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //当前item
                int position = holder.getAdapterPosition();
                Toast.makeText(v.getContext(),bean.get(position).getImageId(),Toast.LENGTH_SHORT).show();
            }
        });

        holder.textView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int position = holder.getAdapterPosition();
                Toast.makeText(v.getContext(),bean.get(position).getName(),Toast.LENGTH_SHORT).show();
            }
        });
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull InnViewHolder holder, int position) {
        ResouceBean resb = bean.get(position);
        Log.d(this.getClass().toString(),resb.getName()+">>>>");
        holder.textView.setText(resb.getName());
        holder.imageView.setImageResource(resb.getImageId());

    }

    @Override
    public int getItemCount() {
        return bean.size();
    }
}

解释:

  1. int position = holder.getAdapterPosition();获取到当前item

15.recylerView作为微信聊天UI

recylerView使用有点复杂,所以本例子主要目的是熟悉recylerView的使用流程.

1.总体布局:

微信的聊天UI上面部分是内容,用来显示聊天的内容,所以是个容器.下面是消息输入框和发送按钮.

UI布局思路,root布局为垂直布局,上部分为聊天的容器,使用recylerView,底部用线性布局,左侧一个输入框,右侧为发送按钮.

另外还需要一个子布局,用来显示每条消息.然后动态加入到recylerView中.

2.具体流程:
使用recylerView作为内容容器.通过发送按钮,将消息输入框的内容动态加入到list中,然后将数据传递个适配器,适配器将数据和消息记录子布局绑定,recylerView在绑定适配器,这样容器就会显示发送的消息.

下面是具体的实现:

1.创建MessageActivity,进行布局:

先不看MessageActivity代码,先看一下布局:

<?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"
    tools:context=".MessageActivity">

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <!--用来显示消息-->
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/messagebox"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"/>

        <!--发送消息表单-->
        <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_height="80dp"
            android:layout_width="match_parent"
            android:orientation="horizontal">

            <EditText
                android:id="@+id/message_text"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:maxLines="2"
                />
            <com.google.android.material.button.MaterialButton
                android:id="@+id/sendmessage_button"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="发送"/>
        </LinearLayout>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

2.创建子布局:

然后在创建一个子布局message_item_layout:
子布局里面有一个线性布局,背景是Nine-Patch图片,是个消息框.里面包含一个文本控件.

<?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="wrap_content"
    android:orientation="vertical">

    <LinearLayout
        android:layout_gravity="right"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:background="@drawable/message">
        <TextView
            android:id="@+id/message_item"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="0dp"
            android:paddingLeft="0dp"
            android:layout_marginRight="10dp"
            />
    </LinearLayout>

</LinearLayout>

Nine-Patch 图片是可以拉伸的图片,可以通过android studio创建,具体做法是选中一个图片,右键选择create-9-patch file.然后可以编辑.

具体的编辑是告诉android那边可以拉伸,哪些不能拉伸,可以拉伸的像素用边框点黑色的点.

上面的点:意思是横向拉伸会复制这个垂直的像素.

左侧的点:意思是纵向拉伸时,可以复制的横向条形像素.

下方的点:黑点连成的线条,意思是文本显示的宽度区域.

右侧的点:黑点连城的线条,意思是文本可以显示的高度区域.

3.创建适配器

我们的数据很简单,只是一个消息,字符串. 适配器必须继承自 RecyclerView.Adapter<MessageAdaptger.InnViewHolder>,其中InnViewHolde是个内部类,内部类用来初始化创建子布局的控件.

适配件内部创建一个list属性,用来存储消息.

然后通过onCreateViewHolder()来创建子布局,将子布局传递给InnViewHolder,InnViewHolder来完成子布局初始化.

通过onBindViewHolder()方法获取当前list中的记录,并且通过InnViewHolder类型的参数,来绑定消息.

具体代码如下:


public class MessageAdaptger extends RecyclerView.Adapter<MessageAdaptger.InnViewHolder> {
    ArrayList<String> data =null;
    public MessageAdaptger(ArrayList list){
        data=list;
    }
    //完成子布局控件的创建
    static class InnViewHolder extends RecyclerView.ViewHolder{
        TextView text;
        public InnViewHolder(@NonNull View itemView) {
            super(itemView);
            text=itemView.findViewById(R.id.message_item);
        }
    }

    //完成子布局创建
    @NonNull
    @Override
    public InnViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view  = LayoutInflater.from(parent.getContext()).inflate(R.layout.message_item_layout,parent,false);
        InnViewHolder holder = new InnViewHolder(view);
        return holder;
    }

    //获取当前显示的list记录,并绑定到子布局控件中
    @Override
    public void onBindViewHolder(@NonNull InnViewHolder holder, int position) {

        String resb =(String) data.get(position);
        Log.d(this.getClass().toString(),resb+">>>>");
        holder.text.setText(resb);
    }
    
    @Override
    public int getItemCount() {
        return data.size();
    }
}

4.完善MessageActivity

package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

import java.util.ArrayList;
import java.util.List;

public class MessageActivity extends AppCompatActivity {

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

        //保存数据
        ArrayList<String> data = new ArrayList<String>();

        //线性布局
        RecyclerView messagebox= findViewById(R.id.messagebox);
        LinearLayoutManager layoutManager = new LinearLayoutManager(this);
        messagebox.setLayoutManager(layoutManager);

        //发送数据,将输入数据绑定到RecyclerView
        EditText text = findViewById(R.id.message_text);
        Button send_button = findViewById(R.id.sendmessage_button);
        send_button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String message=text.getText().toString();
                data.add(message);
                MessageAdaptger adaptger= new MessageAdaptger(data);
                messagebox.setAdapter(adaptger);
            }
        });
    }
}

实际微信分为自己发的,和接收到信息.自己发的在右侧,别人发的在左侧. 如何将发送的,接受的分别显示呢,只要重写适配器中的getItemViewType()方法即可. 前提是消息记录需要是对象,有type类型字段.用来区分是发还是收.

public void getItemViewType(int position ){
       int item = data.get(position);
       return item.type;
}

然后在onCreateViewHolder()方法中,viewType就是item.type参数.

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

IT老卢

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值