《第一行代码》 第三章:UI布局开发

news2024/11/28 4:30:21

一,常用控件的使用方法

1,TextView

   <TextView
        android:id="@+id/text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:textSize="24sp"
        android:textColor="#00ff00"
        android:text="文本"/>

实现效果:
在这里插入图片描述

2,Button

    <Button
        android:id="@+id/button"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="按钮"
        android:textAllCaps="false"/>

绑定事件:

Button button1=(Button) findViewById(R.id.button);
button1.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View view) {
        Intent intent=new Intent(FirstActivity.this,SecondActivity.class);
        startActivity(intent);
    }
});

3,EditText

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/edit_text"
        android:hint="请在这里输入字符"
        android:maxLines="2"/>

按钮获取内容弹窗显示:

        Button button1=(Button) findViewById(R.id.button);
        EditText editText=(EditText) findViewById(R.id.edit_text);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String inputText=editText.getText().toString();
                Toast.makeText(FirstActivity.this, inputText, Toast.LENGTH_SHORT).show();
            }
        });

4,ImageView

    <ImageView
        android:id="@+id/image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/img_1"/>

切换图片:

        Button button1=(Button) findViewById(R.id.button);
        ImageView imageView=(ImageView) findViewById(R.id.image_view);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                    imageView.setImageResource(R.drawable.img_5);
            }
        });

图片地址:
在这里插入图片描述

5,ProgressBar进度条

圆形进度条:

    <ProgressBar
        android:id="@+id/progress_bar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

控制圆形进度条的显隐:

 button1.setOnClickListener(new View.OnClickListener() {
     @Override
     public void onClick(View view) {
         //visible:可见,invisible,不可见但占位,gone不可见不占位
         //View.GONE是内置的常数
         if(progressBar.getVisibility()==View.GONE){
             progressBar.setVisibility(View.VISIBLE);
         }else{
             progressBar.setVisibility(View.GONE);
         }
     }
 });

切换成长条形进度条:

    <ProgressBar
        android:id="@+id/progress_bar"
        style="?android:attr/progressBarStyleHorizontal"
        android:max="100"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

动态设置进度:

        ProgressBar progressBar=(ProgressBar) findViewById(R.id.progress_bar);
        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int progress=progressBar.getProgress();
                progress=progress+10;
                progressBar.setProgress(progress);
            }
        });

6,AlertDialog对话框

        button1.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                AlertDialog.Builder dialog=new AlertDialog.Builder(FirstActivity.this);
                dialog.setTitle("标题提示:");
                dialog.setMessage("信息内容");
                dialog.setPositiveButton("确认", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        //点击确认执行的内容
                    }
                });
                dialog.setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {
                        //点击取消执行的内容
                    }
                });
                dialog.show();
            }
        });

实现的效果:
在这里插入图片描述

7,ProgressDialog

和AlertDialog一样能屏蔽其他控件的交互能力。不同的是他有个loading。

ProgressDialog progressDialog= new ProgressDialog(FirstActivity.this);
progressDialog.setTitle("标题设置");
progressDialog.setMessage("正在加载中……");
progressDialog.setCancelable(true);
progressDialog.show();

实现的效果:
在这里插入图片描述
注意,如果在 setCancelable()中传人了 false,表示 ProgressDialog 是不能通过 Back键取消掉的,这时你就一定要在代码中做好控制,当数据加载完成后必须要调用 ProgressDialog的dismiss()方法来关闭对话框,否则 ProgressDialog将会一直存在。

二,4种基本布局

啥是布局?前端体系里面已经很清晰了,无非就是更好地组织页面元素的一种手段或者说排版。

1,线性布局LinearLayout

页面元素按照线性排列,横向或者是纵向。值得注意的是,它只控制一行或者一列,也就是说垂直布局时,不能把其中一个子元素的宽度设置成100%,这样其他子元素会没地方排列。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮1"
        android:textAllCaps="false"/>
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮2"
        android:textAllCaps="false"/>
    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮3"
        android:textAllCaps="false"/>
</LinearLayout>

实现的效果:
在这里插入图片描述
如果设置成横向 android:orientation=“horizontal”,则是下图这样:
在这里插入图片描述
【android:layout_gravity属性】
他写在布局的元素上面,用于控制子元素在交叉轴的排布方式,和前端的flex布局中的item-self是一样的 效果。
例如:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮1"
        android:layout_gravity="top"
        android:textAllCaps="false"/>
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮2"
        android:layout_gravity="center"
        android:textAllCaps="false"/>
    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:text="按钮3"
        android:textAllCaps="false"/>
</LinearLayout>

实现的效果:
在这里插入图片描述
【android:layout_weight="1"属性】
就和flex布局中的flex-grow差不多,使用这个值的时候,我们通常把该元素的android:layout_width="0dp"设置为0dp,于是元素的宽度就只受layout_weight管控。
layout_weight的计算方式是:先得到全宽(可供设置了layout_weight的元素分配的长度总额),然后按比例分配给对应元素。
例如,有一个输入框和一个按钮。按钮定宽,输入框自适应。则可以这样写:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <EditText
        android:id="@+id/input_message"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:hint="请输入内容"/>
    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="按钮3"
        android:textAllCaps="false"/>
</LinearLayout>

在这里插入图片描述

2,相对布局

参照选父元素时:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_alignParentLeft="true"
        android:text="左上按钮"
        android:textAllCaps="false"/>
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="中间按钮"
        android:textAllCaps="false"/>
</RelativeLayout>

实现效果:
在这里插入图片描述
参照物选同级元素时:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_above="@+id/button2"
        android:layout_toLeftOf="@id/button2"
        android:text="左上按钮"
        android:textAllCaps="false"/>
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="中间按钮"
        android:textAllCaps="false"/>
    <Button
        android:id="@+id/button3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button2"
        android:layout_toLeftOf="@id/button2"
        android:text="左上按钮"
        android:textAllCaps="false"/>
</RelativeLayout>

在这里插入图片描述
还有以同级为参照物,某一边缘对齐。这个属性则是:layout_alignLeft等。

3,帧布局FrameLayout

这种布局没有方便的定位方式,所有的控件都会默认地摆放在布局的左上角。而且是重叠着摆放的。
当然,它可以配合layout_gravity来处理。就和前端中的float布局类似。

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >
    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:text="左上按钮"
        android:textAllCaps="false"/>
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:text="中间按钮"
        android:textAllCaps="false"/>
</FrameLayout>

在这里插入图片描述

4,百分比布局

其实这种布局已经弃用了。
由于linearLayout本身已经支持按比例了,所以百分比布局只是针对Frame-layout和RelativeLayout进行了功能扩展。
不同于前 3 种布局,百分比布局属于新增布局,那么怎么才能做到让新增布局在所有 Android版本上都能使用呢?为此,Android 团队将百分比布局定义在了 support库当中,我们只需要在项目的 build.gradle中添加百分比布局库的依赖,就能保证百分比布局在 Android 所有系统版本上的兼容性了
打开app/buildgradle文件,在dependencies 闭中添加如下内容:

implementation 'androidx.percentlayout:percentlayout:1.0.0'

修改完AS顶部会有这个提示:
在这里插入图片描述
点击Sync Now,gradle就会开始同步了。
布局代码:

<?xml version="1.0" encoding="utf-8"?>
<androidx.percentlayout.widget.PercentFrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <Button
        android:id="@+id/button1"
        android:text="Button 1"

        android:layout_gravity="left|top"
        app:layout_widthPercent="50%"
        app:layout_heightPercent="50%"
        />
    <Button
        android:id="@+id/button2"
        android:text="Button 2"

        android:layout_gravity="right|top"
        app:layout_widthPercent="50%"
        app:layout_heightPercent="50%"
        />
    <Button
        android:id="@+id/button3"
        android:text="Button 3"

        android:layout_gravity="left|bottom"
        app:layout_widthPercent="50%"
        app:layout_heightPercent="50%"
        />
    <Button
        android:id="@+id/button4"
        android:text="Button 4"

        android:layout_gravity="right|bottom"
        app:layout_widthPercent="50%"
        app:layout_heightPercent="50%"
        />


</androidx.percentlayout.widget.PercentFrameLayout>

实现效果:
在这里插入图片描述

三,创建自定义控件

1,自定义和引入布局

只是布局xml罢了(只有ui),而不是前端中的最小功能单元(有ui和事件)

在这里插入图片描述
例如我们要创建标题栏时:
第一步,创建title.xml布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <Button
        android:id="@+id/title_back"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="5dp"
        android:textColor="#fff"
        android:text="返回" />
    <TextView
        android:id="@+id/title_text"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_weight="1"
        android:gravity="center"
        android:text="标题"
        android:textColor="#000000"
        android:textSize="24sp"/>
    <Button
        android:id="@+id/title_edit"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_margin="5dp"
        android:textColor="#fff"
        android:text="编辑" />
</LinearLayout>

第二步:引入title.xml布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <include layout="@layout/title"/>
</LinearLayout>

第三步:屏蔽默认的标题栏:

    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.first_layout);
        ActionBar actionbar=getSupportActionBar();
        if(actionbar!=null){
            actionbar.hide();
        }
    }

实现的效果:
在这里插入图片描述

2,自定义控件

如上文,我们已经在一个活动中创建了标题,但是如果有多个页面都需要这个标题,且有点击事件,那每个活动中都写一次点击事件,多多少少有点冗余。
我们希望像前端的公共组件一样,让他有内置的事件处理。这就需要创建自定义的控件。
第一步:新建com.example.uicustomviews,在其中新建TitleLayout继承自LinearLayout,让它成为我们自定义的标题栏控件。

public class TitleLayout extends LinearLayout {
    //重写它的构造函数
    public TitleLayout(Context context, AttributeSet attrs){
        super(context,attrs);
        //对标题栏布局进行动态加载
        LayoutInflater.from(context).inflate(R.layout.title,this);
    }
}

第二步:在布局文件中添加这个自定义控件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <com.example.uicustomviews.TitleLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>
</LinearLayout>

编译后会发现和之前的写法效果一样。

第三步:在TitleLayout中定义事件。

public class TitleLayout extends LinearLayout {
    //重写它的构造函数
    public TitleLayout(Context context, AttributeSet attrs){
        super(context,attrs);
        //对标题栏布局进行动态加载
        LayoutInflater.from(context).inflate(R.layout.title,this);
        Button titleBack=(Button) findViewById(R.id.title_back);
        Button titleEdit=(Button) findViewById(R.id.title_edit);
        titleBack.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                ((Activity) getContext()).finish();
            }
        });
        titleEdit.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View view) {
                Toast.makeText(getContext(), "点击编辑", Toast.LENGTH_SHORT).show();
            }
        });
    }
}

这样一来,就和前端中的组件一样了,有ui和对应的事件。
过程如下:
在这里插入图片描述

四,最常用的控件ListView

其实就是长列表啦。

1,简单使用

第一步:在layout中使用:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
   <ListView
       android:id="@+id/list_view"
       android:layout_width="match_parent"
       android:layout_height="match_parent"/>
</LinearLayout>

第二步:在活动中传入数据

public class FirstActivity extends BaseActivity {
    //定义的数据,实际应该来自网络请求等
    private String [] data ={
"asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg","asda","adfsg",
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.first_layout);
        //使用ArrayAdapter适配器将数据格式转化成listView可以使用的格式
        ArrayAdapter<String> adapter =new ArrayAdapter<String>(FirstActivity.this,android.R.layout.simple_list_item_1,data);
        ListView listView=(ListView) findViewById(R.id.list_view);
        //将转化后的数据赋值给listView
        listView.setAdapter(adapter);
    }
}

值得注意的是android.R.layout.simple_list_item_1是内置的一个xml文件,里面只用一个TextView,用来简单使用文本。
实现的效果:
在这里插入图片描述

五,更为强大的RecyclerView

它是更强的长列表组件,更推荐使用它。

1,引入包

Recyclerview是一个基于support包的控件,由于目前androidx已经取代了supprot包,因此在此推荐大家使用Androidx的包。
在build.gradle文件中引入如下代码以引入RecyclerView包:

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

2,在主活动中使用

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
   <androidx.recyclerview.widget.RecyclerView
       android:id="@+id/list_main"
       android:layout_width="match_parent"
       android:layout_height="match_parent" />
</LinearLayout>

3,编写适配器

新建FruitAdapter类:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    private List<Fruit> mFruitList;
    //定义了内部类ViewHolder
    static class ViewHolder extends RecyclerView.ViewHolder{
        ImageView fruitImage;
        TextView fruitName;
        //构造函数传入view,通常是recyclerView子项的最外层布局
        public ViewHolder(View view){
            super(view);
            fruitImage=(ImageView) view.findViewById(R.id.fruit_image);
            fruitName=(TextView) view.findViewById(R.id.fruit_name);
        }
    }
    //构造函数,用于把要展示地数据源传进来,并赋值给一个全局变量mFruitList
    public FruitAdapter(List<Fruit> fruitList){
        mFruitList= fruitList;
    }
    //重写方法,用于创建ViewHoler实例,我们在这个方法中将fruit_item布局加载进来,然后创建一个ViewHolder实例。
    //并且把加载出来的布局传入到构造函数中,最后将ViewHolder的实例返回。
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
        ViewHolder holder=new ViewHolder(view);
        return holder;
    }
    //用于对RecyclerView子项的数据进行赋值的,会在每个子项被滚动到屏幕内的时候执行。
    //这里我们通过position参数得到当前项的Fruit实例
    //再将数据设置到ViewHolder的ImageView和TetxView当中
    @Override
    public void onBindViewHolder(@NonNull FruitAdapter.ViewHolder holder, int position) {
        Fruit fruit =mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }
    //它用于告诉RecyclerView一共有多少子项,直接返回数据源的长度就可以了。
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }
}

4,使用适配器

适配器准备好之后,我们就可以开始使用RecyclerView了,修改FirstActivity中的代码:

public class FirstActivity extends BaseActivity {
    //定义的数据,实际应该来自网络请求等
    private List<Fruit> fruitList=new ArrayList<Fruit>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.first_layout);
        initFruits();
        RecyclerView recyclerView=(RecyclerView) findViewById(R.id.recycler_view);
        //使用线性布局
        LinearLayoutManager layoutManager=new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        //将数据传入适配器,构造视图
        FruitAdapter adapter=new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }
    private void initFruits(){
        for (int i=0;i<5;i++){
            Fruit apple=new Fruit("Apple",R.drawable.img_1);
            fruitList.add(apple);
            Fruit banana=new Fruit("banana",R.drawable.img_2);
            fruitList.add(banana);
            Fruit orange=new Fruit("orange",R.drawable.img_3);
            fruitList.add(orange);
            Fruit pear=new Fruit("pear",R.drawable.img_4);
            fruitList.add(pear);
        }
    }
}

其中Fruit类和fruit_item.xml是使用的上一节的代码。

5,实现的效果

在这里插入图片描述

6,实现横向滚动和瀑布流布局

就上文而言,如果改成横向滚动,需要把fruit_item修改成纵向布局:

第一步:修改item的布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="100dp"
    android:layout_height="wrap_content">
    <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"/>
    <TextView
        android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        />

</LinearLayout>

第二步:将默认的纵向布局修改为横向布局

public class FirstActivity extends BaseActivity {
    //定义的数据,实际应该来自网络请求等
    private List<Fruit> fruitList=new ArrayList<Fruit>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.first_layout);
        initFruits();
        RecyclerView recyclerView=(RecyclerView) findViewById(R.id.recycler_view);
        //使用线性布局
        LinearLayoutManager layoutManager=new LinearLayoutManager(this);
        //将默认的纵向布局修改为横向布局
        layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
        recyclerView.setLayoutManager(layoutManager);
        //将数据传入适配器,构造视图
        FruitAdapter adapter=new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }
    private void initFruits(){
        for (int i=0;i<5;i++){
            Fruit apple=new Fruit("Apple",R.drawable.img_1);
            fruitList.add(apple);
            Fruit banana=new Fruit("banana",R.drawable.img_2);
            fruitList.add(banana);
            Fruit orange=new Fruit("orange",R.drawable.img_3);
            fruitList.add(orange);
            Fruit pear=new Fruit("pear",R.drawable.img_4);
            fruitList.add(pear);
        }
    }
}

实现的效果:
在这里插入图片描述

7,瀑布流布局

第一步:修改item的布局

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="5dp"
    >
    <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center_horizontal"/>
    <TextView
        android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:layout_marginTop="10dp"
        />

</LinearLayout>

第二步:修改FirstActivity.xml

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.first_layout);
        initFruits();
        RecyclerView recyclerView=(RecyclerView) findViewById(R.id.recycler_view);
        //修改为栅格布局
        StaggeredGridLayoutManager layoutManager=new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
        recyclerView.setLayoutManager(layoutManager);
        //将数据传入适配器,构造视图
        FruitAdapter adapter=new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }

实现的效果:
在这里插入图片描述

8,它的点击事件

修改适配器文件:

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {
    private List<Fruit> mFruitList;
    //定义了内部类ViewHolder
    static class ViewHolder extends RecyclerView.ViewHolder{
        View fruitView;
        ImageView fruitImage;
        TextView fruitName;
        //构造函数传入view,通常是recyclerView子项的最外层布局
        public ViewHolder(View view){
            super(view);
            fruitView=view;
            fruitImage=(ImageView) view.findViewById(R.id.fruit_image);
            fruitName=(TextView) view.findViewById(R.id.fruit_name);
        }
    }
    //构造函数,用于把要展示地数据源传进来,并赋值给一个全局变量mFruitList
    public FruitAdapter(List<Fruit> fruitList){
        mFruitList= fruitList;
    }
    //重写方法,用于创建ViewHoler实例,我们在这个方法中将fruit_item布局加载进来,然后创建一个ViewHolder实例。
    //并且把加载出来的布局传入到构造函数中,最后将ViewHolder的实例返回。
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.fruit_item,parent,false);
        final ViewHolder holder=new ViewHolder(view);
        holder.fruitView.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                int position=holder.getAdapterPosition();
                Fruit fruit =mFruitList.get(position);
                Toast.makeText(v.getContext(), fruit.getName(), Toast.LENGTH_SHORT).show();
            }
        });
        holder.fruitImage.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v){
                int position=holder.getAdapterPosition();
                Fruit fruit =mFruitList.get(position);
                Toast.makeText(v.getContext(), "点击了图片", Toast.LENGTH_SHORT).show();
            }
        });
        return holder;
    }
    //用于对RecyclerView子项的数据进行赋值的,会在每个子项被滚动到屏幕内的时候执行。
    //这里我们通过position参数得到当前项的Fruit实例
    //再将数据设置到ViewHolder的ImageView和TetxView当中
    @Override
    public void onBindViewHolder(@NonNull FruitAdapter.ViewHolder holder, int position) {
        Fruit fruit =mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }
    //它用于告诉RecyclerView一共有多少子项,直接返回数据源的长度就可以了。
    @Override
    public int getItemCount() {
        return mFruitList.size();
    }
}

六,编写页面的最佳实践

1,制作Nine-Patch图片

这种格式的图片可以指定哪些区域可以拉伸,哪些区域不可以。
在这里插入图片描述

2,修改firstLayout.xml测试下

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/message_left"
    >
</LinearLayout>

在这里插入图片描述

3,开始编写聊天界面

先用同样的方法制作message_right图片。
因为我们需要使用到recyclerView所以按照上文的方法,需要先引入。
在build.gradle文件中引入如下代码以引入RecyclerView包:

dependencies {
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

编写主活动的页面布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#d8e0e8"
    >
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <EditText
            android:id="@+id/input_text"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="请输入文本"
            android:maxLines="2"/>
        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="发送"/>

    </LinearLayout>
</LinearLayout>

然后新建Msg类:

public class Msg {
    //表示是收到的信息
    public static final int TYPE_RECEIVED=0;
    //表示是发出的信息
    public static final int TYPE_SEND=1;
    public String content;
    private int type;
    public Msg(String content,int type){
        this.content=content;
        this.type=type;
    }
    public String getContent(){
        return content;
    }
    public int getType(){
        return type;
    }
}

接下来编写recyclerView子项的布局,新建msg_item.xml文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp">

    <LinearLayout
        android:id="@+id/left_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:background="@drawable/message_left"
        android:orientation="horizontal">
        <TextView
            android:id="@+id/left_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="10dp"
            android:textColor="#000"/>
    </LinearLayout>
    <LinearLayout
        android:id="@+id/right_layout"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:background="@drawable/message_right"
        android:orientation="horizontal">
        <TextView
            android:id="@+id/right_msg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="10dp"
            android:textColor="#000"/>
    </LinearLayout>
</LinearLayout>

接下来创建适配器,新建类MsgAdapter:

public class MsgAdapter extends RecyclerView.Adapter<MsgAdapter.ViewHolder> {
    private List<Msg> mMsgList;
    //定义了内部类ViewHolder
    static class ViewHolder extends RecyclerView.ViewHolder{
        LinearLayout leftLayout;
        LinearLayout rightLayout;
        TextView leftMsg;
        TextView rightMsg;
        //构造函数传入view,通常是recyclerView子项的最外层布局
        public ViewHolder(View view){
            super(view);
            leftLayout=(LinearLayout) view.findViewById(R.id.left_layout);
            rightLayout=(LinearLayout) view.findViewById(R.id.right_layout);
            leftMsg=(TextView) view.findViewById(R.id.left_msg);
            rightMsg=(TextView) view.findViewById(R.id.right_msg);
        }
    }
    //构造函数,用于把要展示地数据源传进来,并赋值给一个全局变量mFruitList
    public MsgAdapter(List<Msg> msgList){
        mMsgList= msgList;
    }
    //重写方法,用于创建ViewHoler实例,我们在这个方法中将fruit_item布局加载进来,然后创建一个ViewHolder实例。
    //并且把加载出来的布局传入到构造函数中,最后将ViewHolder的实例返回。
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item,parent,false);
        ViewHolder holder=new ViewHolder(view);
        return holder;
    }
    //用于对RecyclerView子项的数据进行赋值的,会在每个子项被滚动到屏幕内的时候执行。
    //这里我们通过position参数得到当前项的Fruit实例
    //再将数据设置到ViewHolder的ImageView和TetxView当中
    @Override
    public void onBindViewHolder(@NonNull MsgAdapter.ViewHolder holder, int position) {
        Msg msg =mMsgList.get(position);
       if(msg.getType()==Msg.TYPE_RECEIVED){
           //左侧收到
           holder.leftLayout.setVisibility(View.VISIBLE);
           holder.rightLayout.setVisibility(View.GONE);
           holder.leftMsg.setText(msg.getContent());
       }else if(msg.getType()==Msg.TYPE_SEND){
           //右侧收到
           holder.leftLayout.setVisibility(View.GONE);
           holder.rightLayout.setVisibility(View.VISIBLE);
           holder.rightMsg.setText(msg.getContent());
       }
    }
    //它用于告诉RecyclerView一共有多少子项,直接返回数据源的长度就可以了。
    @Override
    public int getItemCount() {
        return mMsgList.size();
    }
}

然后再在主活动中传入数据:

public class FirstActivity extends BaseActivity {
    private List<Msg> msgList=new ArrayList<>();
    private EditText inputText;
    private Button send;
    private RecyclerView msgRecyclerView;
    private MsgAdapter adapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.first_layout);
        initMsgs();
        inputText=(EditText) findViewById(R.id.input_text);
        send =(Button) findViewById(R.id.send);
        msgRecyclerView=(RecyclerView) findViewById(R.id.recycler_view);
        LinearLayoutManager layoutManager =new LinearLayoutManager(this);
        //将信息显示区域设定成线性布局
        msgRecyclerView.setLayoutManager(layoutManager);
        //传入数据给适配器处理
        adapter=new MsgAdapter(msgList);
        msgRecyclerView.setAdapter(adapter);

        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                String content =inputText.getText().toString();
                if(!"".equals(content)){
                    Msg msg=new Msg(content,Msg.TYPE_SEND);
                    msgList.add(msg);
                    //当有新消息时,刷新显示
                    adapter.notifyItemInserted(msgList.size()-1);
                    //将ListView定位到最后一行
                    msgRecyclerView.scrollToPosition(msgList.size()-1);
                    inputText.setText("");
                }
            }
        });
    }
    private void initMsgs(){
        Msg msg1=new Msg("hello guy",Msg.TYPE_RECEIVED);
        msgList.add(msg1);
        Msg msg2=new Msg("hello what is that?",Msg.TYPE_SEND);
        msgList.add(msg2);
        Msg msg3=new Msg("煞笔吧,会说中文拽啥英文",Msg.TYPE_RECEIVED);
        msgList.add(msg3);
    }
}

实现的效果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/335707.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

iptables防火墙SNAT和DNAT

iptables防火墙SNAT和DNAT一、SNAT原理和应用1、SNAT原理2、SNAT应用环境3、SNAT转换前提条件二、SNAT案列1、实验需求2、实验环境3、实验目的三、DNAT原理和应用1、DNAT原理2、DNAT 应用环境3、DNAT转换的前提条件四、DNAT案列总结一、SNAT原理和应用 1、SNAT原理 SNAT原理:…

365天深度学习训练营 第P6周:好莱坞明星识别

&#x1f368; 本文为&#x1f517;365天深度学习训练营 内部限免文章&#xff08;版权归 K同学啊 所有&#xff09;&#x1f366; 参考文章地址&#xff1a; &#x1f517;第P6周&#xff1a;好莱坞明星识别 | 365天深度学习训练营&#x1f356; 作者&#xff1a;K同学啊 | 接…

扩散模型diffusion model用于图像恢复任务详细原理 (去雨,去雾等皆可),附实现代码

文章目录1. 去噪扩散概率模型2. 前向扩散3. 反向采样3. 图像条件扩散模型4. 可以考虑改进的点5. 实现代码1. 去噪扩散概率模型 扩散模型是一类生成模型, 和生成对抗网络GAN 、变分自动编码器VAE和标准化流模型NFM等生成网络不同的是, 扩散模型在前向扩散过程中对图像逐步施加噪…

如何为自己的应用选择数据库?有这些考虑因素

节选翻译自 Michal Toiba 的博客 微软前不久宣布推出分布式关系数据库 Azure Cosmos DB for PostgreSQL&#xff0c;使 Azure 成为第一个在单一数据库服务中同时支持关系和 NoSQL&#xff08;非关系&#xff09;数据的云平台。这意味着 Azure Cosmos DB 开发者在构建云原生应用…

只需三步,完成ChatGPT微信机器人搭建

大家好&#xff0c;我是可乐。 这两天 Chatgpt 又爆火了&#xff0c;去年12月份刚出来的时候&#xff0c;我写了两篇文章&#xff1a; ①、如何注册Chatgpt&#xff1f; ②、如何将 chatgpt接入微信&#xff1f; 然后沉寂了一个月&#xff0c;没想现在到又火了。本篇文章我…

计算机组成与体系结构 性能设计 William Stallings 第2章 性能问题

2.1 优化性能设计例如&#xff0c;当前需要微处理器强大功能的桌面应用程序包括&#xff1a;图像处理、三维渲染、语音识别、视频会议、多媒体创作、文件的声音和视频注释、仿真建模从计算机组成与体系结构的角度来看&#xff0c;一方面&#xff0c;现代计算机的基本组成与50多…

强大的ChatGpt为企业营销推广提供了全方位的加持

chatgpt&#xff0c;一个火出圈的“聊天机器人”。从写作文&#xff0c;到写代码&#xff0c;似乎没有什么是它干不了的。 ChatGpt在工业中的应用场景有哪些? 在工业领域&#xff0c;它可以用于提高生产效率&#xff0c;缩短生产周期&#xff0c;并帮助工人解决生产过程中的问…

优先级队列(PriorityQueue 和 Top-K问题)

一、PriorityQueue java中提供了两种优先级队列&#xff1a;PriorityQueue 和 PriorityBlockingQueue。其中 PriorityQueue 是线程不安全的&#xff0c;PriorityBolckingQueue 是线程安全的。 PriorityQueue 使用的是堆&#xff0c;且默认情况下是小堆——每次获取到的元素都是…

LeetCode刷题------字符串

LeetCode&#xff1a;344.反转字符串 定义两个指针&#xff08;也可以说是索引下标&#xff09;&#xff0c;一个从字符串前面&#xff0c;一个从字符串后面&#xff0c;两个指针同时向中间移动&#xff0c;并交换元素。 var reverseString function(s) {let l -1, r s.len…

Windows下LuaBridge2.8的环境配置及简单应用

Windows下LuaBridge2.8的环境配置及简单应用 LuaBridge2.8下载链接&#xff1a; https://github.com/vinniefalco/LuaBridge/tags 关于Lua的环境配置可参考以下链接&#xff08;这里不做简述&#xff09;&#xff1a; https://ufgnix0802.blog.csdn.net/article/details/125341…

什么是抗压能力?抗压能力的重要性及提高方法

在现实生活中每个人都面临着或大或小的压力。不论是学习还是工作&#xff0c;没有压力就没有进步和提升&#xff0c;压力可以转化为动力&#xff0c;而扛不住压力那就会导致躺平的结果。在职业的道路上&#xff0c;压力一直都存在&#xff0c;尤其是站在企业人力资源管理的角度…

开学季,关于校园防诈骗宣传,如何组织一场微信线上答题考试

开学季&#xff0c;关于校园防诈骗宣传&#xff0c;如何组织一场微信线上答题考试如何组织一场微信线上答题考试在线考试是一种非常节约成本的考试方式&#xff0c;考生通过微信扫码即可参加培训考试&#xff0c;不受时间、空间的限制&#xff0c;近几年越来越受企事业单位以及…

Navicat远程连接mysql教程及Navicat报错10061解决办法

文章目录想要远程连接的前提是数据库要配置密码&#xff0c;配置密码参考如下进入要连接的数据库看一下原有的配置&#xff0c;host 和 user修改配置授权root用户进行远程登录配置修改完成后&#xff0c;打开navicat&#xff0c;新建连接点击测试链接&#xff0c;显示连接后点击…

10个自动化测试框架,测试工程师用起来

软件行业正迈向自主、快速、高效的未来。为了跟上这个高速前进的生态系统的步伐&#xff0c;必须加快应用程序的交付时间&#xff0c;但不能以牺牲质量为代价。快速实现质量是必要的&#xff0c;因此质量保证得到了很多关注。为了满足卓越的质量和更快的上市时间的需求&#xf…

ThinkPad笔记本如何拆卸及安装电池

注意:外置电池可以自行拆卸&#xff0c;内置电池如需拆卸建议联系服务商&#xff0c;由工程师协助操作。 外置电池的拆卸方法&#xff1a; 方式一.部分机型只有一个锁扣&#xff0c;将锁扣移动到解锁状态&#xff0c;然后将电池向机身外侧抽出&#xff08;以T420为例&#xf…

用Qt开发的ffmpeg流媒体播放器,支持截图、录像,支持音视频播放,支持本地文件播放、网络流播放

前言 本工程qt用的版本是5.8-32位&#xff0c;ffmpeg用的版本是较新的5.1版本。它支持TCP或UDP方式拉取实时流&#xff0c;实时流我采用的是监控摄像头的RTSP流。音频播放采用的是QAudioOutput&#xff0c;视频经ffmpeg解码并由YUV转RGB后是在QOpenGLWidget下进行渲染显示。本…

如何使用ModelScope训练自有的远场语音唤醒模型?

就像人和人交流时先会喊对方的名字一样&#xff0c;关键词就好比智能设备的"名字"&#xff0c;而关键词检测模块则相当于交互流程的触发开关。 本文介绍魔搭社区中远场语音增强与唤醒一体化的语音唤醒模型的构成、体验方式&#xff0c;以及如何基于开发者自有数据进…

MySQL数据库07——高级条件查询

前面一章介绍了基础的一个条件的查询&#xff0c;如果多条件&#xff0c;涉及到逻辑运算&#xff0c;and or 之类的。就是高级一点的条件查询。本章来介绍复杂的条件搜索表达式。 AND运算符 AND运算符只有当两边操作数均为True时&#xff0c;最后结果才为True。人们使用AND描述…

高性能IO模型:为什么单线程Redis能那么快?

我们通常说Redis是单线程&#xff0c;主要是指Redis的网络IO和键值对读写是由一个线程来完成的。这也是Redis对外提供键值存储服务的主要流程。 但redis的其他功能&#xff0c;比如持久化、异步删除、集群数据同步等&#xff0c;其实是由额外的线程执行的。 Redis为什么用单线…

探讨MySQL事务特性和实现原理

一、概念 事务 一般指的是逻辑上的一组操作&#xff0c;或者作为单个逻辑单元执行的一系列操作&#xff0c;一个事务中的所有操作会被封装成一个不可分割的执行单元&#xff0c;这个单元的所有操作要么全部执行成功&#xff0c;要么全部执行失败&#xff0c;只要其中任意一个操…