第一行代码——第三章:软件也要拼脸蛋——UI开发的点点滴滴

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lhk147852369/article/details/84339265

目录:

3.1 如何编写程序界面

3.2 常用控件的使用方法

3.2.1TextView

3.2.2 Button

3.2.3 EditText

3.2.4 ImageView

3.2.5 ProgressBar

3.2.6 AlertDialog

3.2.7 ProgressDialog

3.3 详解4种常用布局

3.3.1 线性布局

3.3.2 相对布局

3.3.3 帧布局

3.3.4 百分比布局

3.4 系统控件不够用?创建自定义控件

3.4.1 引入布局

3.4.2 创建自定义控件

3.5 最常用和最难用的控件——ListView

3.5.1 ListView的简单用法

3.5.2 定制ListView的界面

3.5.3 提升ListView的运行效率

3.5.4 ListView的点击事件

3.6 更强大的滚动控件——RecyclerView

3.6.1 RecyclerView的基本用法

3.6.2 实现横向滚动和瀑布流布局

3.6.3 RecyclerView的点击事件

3.7 编写界面的最佳实践

3.7.1 制作Nine-Patch图片

3.7.2 编写精美的聊天布局

3.8 小结与点评


知识点:

3.1 如何编写程序界面

Android中有多种编写程序界面的方式可供选择。Android Studio 和Eclipse中都提供了相应的可视化编辑器,允许使用拖放控件的方式来编写布局,并能在视图上直接修改控件的属性。不过我并不推荐你使用这种方式来编写界面,因为可视化编辑工具并不利于你去真正了解界面背后的实现原理。通过这种方式制作出的界面通常不具有很好的屏幕适配性,而且当需要编写较为复杂的界面时,可视化编辑工具将很难胜任。因此本书中所有的界面都将通过最基本的方式去实现,即编写XML代码。等你完全掌握了使用XML来编写界面的方法之后,不管是进行高复杂度的界面实现,还是分析和修改当前现有界面,对你来说都将是手到擒来。

3.2 常用控件的使用方法

3.2.1TextView

显示一段文本信息

   <TextView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="HelloWord"
        />

3.2.2 Button

用户交互的控件(比如,点击跳转) 

<Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Im Button"
        />

3.2.3 EditText

输入 和编辑内容(如:微信发送的消息)  

   <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="请输入内容"
        />

3.2.4 ImageView

图片控件  用于显示图片

<ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@mipmap/ic_launcher"
        />

3.2.5 ProgressBar

 进度条

Style不写 默认 圆形进度条 ?android:attr/progressBarStyleHorizontal

  <ProgressBar
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        style="?android:attr/progressBarStyleHorizontal"
        />

3.2.6 AlertDialog

可以在当前的界面弹出一个对话框

 new AlertDialog.Builder(this)
                .setTitle("确定要删除吗?")
                .setMessage("Something important.")
                .setPositiveButton("确定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {

                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialogInterface, int i) {

                    }
                })
                .show();

3.2.7 ProgressDialog

进度条弹框

 final ProgressDialog dialog = new ProgressDialog(this);
        dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);// 设置水平进度条
        dialog.setCancelable(true);// 设置是否可以通过点击Back键取消
        dialog.setCanceledOnTouchOutside(false);// 设置在点击Dialog外是否取消Dialog进度条
        dialog.setIcon(R.drawable.ic_launcher);// 设置提示的title的图标,默认是没有的
        dialog.setTitle("提示");
        dialog.setMax(100);
        dialog.setButton(DialogInterface.BUTTON_POSITIVE, "确定",
                new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // TODO Auto-generated method stub

                    }
                });
        dialog.setButton(DialogInterface.BUTTON_NEGATIVE, "取消",
                new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // TODO Auto-generated method stub

                    }
                });
        dialog.setButton(DialogInterface.BUTTON_NEUTRAL, "中立",
                new DialogInterface.OnClickListener() {

                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // TODO Auto-generated method stub

                    }
                });
        dialog.setMessage("这是一个水平进度条");
        dialog.show();
        new Thread(new Runnable() {

            @Override
            public void run() {
                // TODO Auto-generated method stub
                int i = 0;
                while (i < 100) {
                    try {
                        Thread.sleep(200);
                        // 更新进度条的进度,可以在子线程中更新进度条进度
                        dialog.incrementProgressBy(1);
                        // dialog.incrementSecondaryProgressBy(10)//二级进度条更新方式
                        i++;

                    } catch (Exception e) {
                        // TODO: handle exception
                    }
                }
                // 在进度条走完时删除Dialog
                dialog.dismiss();

            }
        }).start();

3.3 详解4种常用布局

3.3.1 线性布局

LinearLayout又称作线性布局,是一种非常常用的布局。

android:orientation="horizontal" 默认水平    vertical  修改后横向 

 <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">


    </LinearLayout>

3.3.2 相对布局

可以通过相对定位的方式让空间出现在布局的任何位置。

如下让Button2 在Button1 右侧

 <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        
        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" 
            android:id="@+id/btn_1"
            />

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btn_2"
            android:layout_toRightOf="@+id/btn_1"
            />

    </RelativeLayout>

3.3.3 帧布局

应用场景也少了很多,所有控件都会显示在布局的左上角

<FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <Button
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/btn_1"
            />

    </FrameLayout>

3.3.4 百分比布局

前面3 种布局都是从Android 1.0版本中就开始支持了,一直沿用到现在,但是特殊情况以上布局实现起来很不支持的

百分比库 地址 使用方式 请同时查看
https://github.com/JulienGenoud/android-percent-support-lib-sample

3.4 系统控件不够用?创建自定义控件

当系统自带的控件不能支持我们的需求时,就需要自定义空控件了。

3.4.1 引入布局

3.4.2 创建自定义控件

这2章主要是讲 新建一个xml文件 设置标题栏的布局

并不是我们所想的自定义View

3.5 最常用和最难用的控件——ListView

由于屏幕控件比较有限,能够一次性在屏幕上显示的内容不多,所以有大数据的时候需要用到列表

3.5.1 ListView的简单用法

在布局中加入ListView 控件,并为ListView 指定了一个id 设置成match_parent 占满整个空间

  <ListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/list_view"
        >
    </ListView>

在Activity中

public class MainActivity extends Activity {  
private String[] data = { "Apple", "Banana", "Orange", "Watermelon",  
"Pear", "Grape", "Pineapple", "Strawberry", "Cherry", "Mango" };  
@Override  
protected void onCreate(Bundle savedInstanceState) {  
super.onCreate(savedInstanceState);  
setContentView(R.layout.activity_main);  
ArrayAdapter<String> adapter = new ArrayAdapter<String>(  
MainActivity.this, android.R.layout.simple_list_item_1, data);  
ListView listView = (ListView) findViewById(R.id.list_view);  
listView.setAdapter(adapter);  
}  
}  

在这里我运用了系统包含的一个TextView的布局文件:android.R.layout.simple_expandable_list_item_1,调用这个比较方便,

ArrayAdapter<String> adapter = new ArrayAdapter<String>(  MainActivity.this, android.R.layout.simple_list_item_1, data);  的意思是:创建一个数组适配器的代码,里面有三个参数,第一个参数是上下文,就是当前的Activity, 第二个参数是android sdk中自己内置的一个布局,它里面只有一个TextView,这个参数是表明我们数组中每一条数据的布局是这个view,就是将每一条数据都显示在这个 view上面;第三个参数就是我们要显示的数据。listView会根据这三个参数,遍历data里面的每一条数据,读出一条,显示到第二 个参数对应的布局中,这样就形成了我们看到的listView.

ArrayAdapter是BaseAdapter的子类

3.5.2 定制ListView的界面

定义一个实体类Fruit


public class Fruit {
    private String name;
    private int imageId;
 
    public Fruit(String name, int imageId) {
        this.name = name;
        this.imageId = imageId;
    }
 
    public String getName() {
        return name;
    }
 
    public int getImageId() {
        return imageId;
    }
}

为 ListView 的子项指定一个我们自定义的布局 fruit_item.xml。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <ImageView
        android:id="@+id/fruit_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
 
    <TextView
        android:id="@+id/fruit_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:layout_marginLeft="10dip" />
</LinearLayout>

创建一个自定义的适配器 FruitAdapter,这个适配器继承自 ArrayAdapter。重写构造方法和 getView 方法。


 
public class FruitAdapter extends ArrayAdapter{
    private final int resourceId;
 
    public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects) {
        super(context, textViewResourceId, objects);
        resourceId = textViewResourceId;
    }
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        Fruit fruit = (Fruit) getItem(position); // 获取当前项的Fruit实例
        View view = LayoutInflater.from(getContext()).inflate(resourceId, null);//实例化一个对象
        ImageView fruitImage = (ImageView) view.findViewById(R.id.fruit_image);//获取该布局内的图片视图
        TextView fruitName = (TextView) view.findViewById(R.id.fruit_name);//获取该布局内的文本视图
        fruitImage.setImageResource(fruit.getImageId());//为图片视图设置图片资源
        fruitName.setText(fruit.getName());//为文本视图设置文本内容
        return view;
    }
}     

View view = LayoutInflater.from(getContext()).inflate(resourceId, null);使用Inflater对象来将布局文件解析成一个View

在MainActivity中编写,初始化水果数据


 
public class MainActivity extends Activity {
    private List<Fruit> fruitList = new ArrayList<Fruit>();
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits(); // 初始化水果数据
        FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList);
        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);
    }
 
    private void initFruits() {
        Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
        fruitList.add(apple);
        Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
        fruitList.add(banana);
        Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
        fruitList.add(orange);
        Fruit watermelon = new Fruit("Watermelon", R.drawable.watermelon_pic);
        fruitList.add(watermelon);
        Fruit pear = new Fruit("Pear", R.drawable.pear_pic);
        fruitList.add(pear);
        Fruit grape = new Fruit("Grape", R.drawable.grape_pic);
        fruitList.add(grape);
        Fruit pineapple = new Fruit("Pineapple", R.drawable.pineapple_pic);
        fruitList.add(pineapple);
        Fruit strawberry = new Fruit("Strawberry", R.drawable.strawberry_pic);
        fruitList.add(strawberry);
        Fruit cherry = new Fruit("Cherry", R.drawable.cherry_pic);
        fruitList.add(cherry);
        Fruit mango = new Fruit("Mango", R.drawable.mango_pic);
        fruitList.add(mango);
    }
}

运行效果图

这样一个简单的ListView界面就完成了

3.5.3 提升ListView的运行效率

  •  新增加一个内部类ViewHolder,对控件进行缓存。
  • 复用convertView
  • 减少findViewById的次数。

3.5.4 ListView的点击事件

以下为单个item点击事件

listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
          @Override
          public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
              
          }
      });

如果要实现子view点击事件 可以写接口实现

3.6 更强大的滚动控件——RecyclerView

增加版的ListView优化Listview中的各种问题,扩展性要好。

3.6.1 RecyclerView的基本用法

没有添加build gradle 的 添加依赖库 

版本号 可改

implementation 'com.android.support:recyclerview-v7:27.1.1'

添加RecyclerView

  <android.support.v7.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

同上使用水果类就可以

新建Adapter 

我这里用了ButterKnife 注解

package com.dak.administrator.firstcode.material_design;

import android.content.Context;
import android.content.Intent;
import android.support.annotation.NonNull;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import com.bumptech.glide.Glide;
import com.dak.administrator.firstcode.R;

import java.util.List;

import butterknife.BindView;
import butterknife.ButterKnife;

/**
 * Created by Administrator on 2018/11/7.
 */

public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder> {

    private Context mContext;

    private List<Fruit> mFruitList;

    public FruitAdapter(List<Fruit> fruitList) {
        mFruitList = fruitList;
    }

    @NonNull
    @Override
    public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if (mContext == null) {
            mContext = parent.getContext();
        }

        View view = LayoutInflater.from(mContext).inflate(R.layout.fruit_item, parent, false);
        final ViewHolder holder = new ViewHolder(view);
        holder.cardView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int postion = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(postion);
                Intent intent = new Intent(mContext, CollapseActivity.class);
                intent.putExtra(CollapseActivity.FRUIT_NAME, fruit.getName());
                intent.putExtra(CollapseActivity.FRUIT_IMAGE_ID, fruit.getImageId());
                mContext.startActivity(intent);

            }
        });
        return holder;
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);

        holder.fruitName.setText(fruit.getName());
        Glide.with(mContext).load(fruit.getImageId()).into(holder.fruitImage);

    }

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


    static class ViewHolder extends RecyclerView.ViewHolder{
        @BindView(R.id.fruit_image)
        ImageView fruitImage;
        @BindView(R.id.fruit_name)
        TextView fruitName;
        @BindView(R.id.card_view)
        CardView cardView;

        public ViewHolder(View view) {
            super(view);
            ButterKnife.bind(this, view);
        }
    }
}

Activity调用

 adapter = new FruitAdapter(fruitsList);
        GridLayoutManager layoutManager = new GridLayoutManager(this, 2);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setAdapter(adapter);

3.6.2 实现横向滚动和瀑布流布局

横向滚动只要把 之前GridLayoutManager 改成如下即可 

LinearLayoutManager默认 竖向

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);

瀑布流:

StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);

当然瀑布流是存在着许多坑 请看

https://www.jianshu.com/p/b0f80b1c29d0

https://blog.csdn.net/lhk147852369/article/details/84346240

3.6.3 RecyclerView的点击事件

点击事件已经在上代码加上了 在ViewHolder当中 这是在adapter的
如果需要在Activity或其他地方 可以写接口实现,这里不说了。

http://www.jcodecraeer.com/plus/view.php?aid=7881

3.7 编写界面的最佳实践

3.7.1 制作Nine-Patch图片

https://blog.csdn.net/lhk147852369/article/details/84346031

3.7.2 编写精美的聊天布局

主要是在onBindViewHolder 判断传过来的信息是发送的还是接收的

通过Type值 去设置Visible 或者Gone 

当然还有另外方法

通过getItemViewType方法

https://www.cnblogs.com/android-blogs/p/5690853.html

3.8 小结与点评

郭霖总结:

虽然本章的内容很多,但我觉得学习起来应该还是挺愉快的吧。不同于上一章中我们来来回回使用那几个按钮,本章可以说是使用了各种各样的控件,制作出了丰富多彩的界面。尤其是在实战环节,编写出了那么精美的聊天界面,你的满足感应该比上一章还要强吧?

本章从Android中的一些常见控件开始人手,依次介绍了基本布局的用法、自定义控件的方法、ListView 的详细用法以及RecyclerView的使用,基本已经将重要的UI知识点全部覆盖了。想想在开始的时候我说不推荐使用可视化的编辑工具,而是应该全部使用XML的方式来编写界面,现在你是不是已经感觉使用XML非常简单了呢?以后不管面对多么复杂的界面,我希望你都能够自信满满,因为真正理解了界面编写的原理之后,是没有什么能够难得倒你的。
不过到目前为止,我们还只是学习了Android 手机方面的开发技巧,下一章将会涉及一些Android平板方面的知识点,能够同时兼容手机和平板也是自Android4.0 系统开始就支持的特性。适当地放松和休息一段时间后, 我们再来继续前行吧!

我的总结:

这章还是比较重要的,因为在工作中接触到的很多都是和列表有关的。

猜你喜欢

转载自blog.csdn.net/lhk147852369/article/details/84339265