第 3 章 - UI界面

1.常用控件:

Button:

android:textAllCaps="false"
—— 用于设定文本是否大写

EditText:

android:hint="Something"
—— 指定一段提示性文本
android:inputType="password"
—— 设定输入类型
android:maxLines="2"
—— 设定最大行数

ImageView:

android:src="@drawable/img_1"
—— 指定一张图片
setImageResource(R.drawable.img_2)
—— 设定图片资源

ProgressBar:

android:visibility="visible"/"invisible"/"gone"
—— 设定可见属性
setVisibility(View.VISIBLE/View.INVISIBLE/View.GONE)
—— 设定可见属性
getVisibility()
—— 获取当前可见属性
style="?android:attr/progressBarStyleHorizontal"
android:max="100"
—— 设定水平进度条,并限制最大值为 100
getProgress() setProgress()
—— 获取当前进度和设置进度

AlertDialog:

new AlertDialog.Builder(Context context)
—— 创建警告对话框
setTitle()
—— 设定标题
setMessage
—— 设定内容
setCancelable
—— 设定是否能返回取消对话框
setPositiveButton()
—— 设置确定按钮
setNegativeButton()
—— 设置取消按钮
show()
—— 将对话框显示出来

ProgressDialog:

new ProgressDialog(Context context)
—— 创建进度对话框
setTitle()
—— 设定标题
setMessage
—— 设定内容
setCancelable
—— 设定是否能返回取消对话框
show()
—— 将对话框显示出来
dismiss()
—— 关闭对话框
setIndeterminate()
—— 设定不确定状态

2.详解基本布局:

RelativeLayout:
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_centerInParent="true"

—— 根据父布局来摆放控件

android:layout_above="@id/button"
android:layout_below="@id/button"
android:layout_toLeftOf="@id/button"
android:layout_toRightOf="@id/button"

—— 根据控件来摆放

android:layout_alignLeft="@id/button"
android:layout_alignTop="@id/button"

—— 对齐控件边缘,当一个控件去引用另一个控件的 id 时,该控件一定要定义在引用控件的后面,不然会出现找不到 id 的情况

PercentLayout:

implementation 'com.android.support:percent:24.2.1'
—— 在 app/build.gradledependencies 闭包中添加依赖声明
android.support.percent.PercentFrameLayout
xmlns:app="http://schemas.android.com/apk/res-auto"
—— 在布局文件中添加完整包名app命名空间
app:layout_widthPercent="50%"
app:layout_heightPercent="50%"
—— 设定空间百分比大小

3.自定义 View:

所有控件都是直接或间接继承自 View,所用的所有布局都是直接或间接继承自 ViewGroupView 是 Android 中最基本的一种 UI组件,而 ViewGroup 是一种特殊的 View,它可以包含很多 子View子ViewGroup,是一个用于放置控件和布局的容器

引入布局:
  • 重新定义一个 title.xml 标题栏布局,在 activity_main.xml 中引入
    <include layout="@layout/title"/>

  • 将系统自带的标题栏隐藏

ActionBar actionBar = getSupportActionBar();
if(actionBar != null){
    actionBar.hide();
}
  • 创建自定义控件:
    ——新建一个 java 文件,让其作为标题栏控件,并编写具体逻辑操作
Public class TitleLayout extends LinearLayout implements View.OnClickListener{
    public TitleLayout(Context context, AttributeSet attrs){
        super(context, attrs);
        /**
         * inflate() 第二个参数是给加载好的布局再添加一个父布局
         * 这里指定为 TitleLayout
         */
        LayoutInflater.from(context).inflate(R.layout.title, this);
        Button titleBack = findViewById(R.id.title_back);
        Button titleEdit = findViewById(R.id.title_edit);
        titleBack.setOnClickListener(this);
        titleEdit.setOnClickListener(this);
    }
    @Override
    public void onClick(View v){
        switch (v.getId()){
            case R.id.title_back:
                // 编写逻辑
                break;
            case R.id.title_edit:
                // 编写逻辑
                break;
        }
    }
}

——在 activity_main.xml 中添加此自定义控件

<com.example.uitest.TitleLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

4.探究 ListView:

简单用法:
  • 直接在 activity_main.xml 中加入控件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
    
</LinearLayout>
  • 修改 MainActivity.java
public class MainActivity extends AppCompatActivity {

    /**
     * 简单使用 String 类型数组来测试
     */
    private String[] data = {"Apple", "Pear", "Grape", "Banana", "Orange", "Cherry",
            "Apple", "Pear", "Grape", "Banana", "Orange", "Cherry", "Watermelon",
            "Apple", "Pear", "Grape", "Banana", "Orange", "Cherry", "Watermelon",
            "Apple", "Pear", "Grape", "Banana", "Orange", "Cherry", "Watermelon",};

    /**
     * 数组中的数据无法直接传递给 ListView ,因此需要借助适配器来完成,
     * 新建一个数组适配器,泛型指定为 String ,这里使用了系统内置的布局
     * android.R.layout.simple_list_item_1 作为 ListView 子项布局的 id ,
     * 最后调用 ListView 的 setAdapter() 方法将构建好的适配器对象传入,
     * 这样 ListView 和数据之间的关联就建立完成了。
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ArrayAdapter<String> adapter = new ArrayAdapter<String>
                (this,android.R.layout.simple_list_item_1, data);
        ListView listView = findViewById(R.id.list_view);
        listView.setAdapter(adapter);
    }
}
步骤:
  • 添加 ListView 控件
  • 添加需要适配的数据
  • 生成适配器对象,泛型指定为数据类型
  • 生成 ListView 的实例
  • 调用 ListViewsetAdapter() 方法,将构建好的适配器对象传入
定制 ListView 的界面:
  • 定义一个类作为 ListView适配器 的适配类型:
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子项布局
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/fruit_image"
        android:layout_height="wrap_content"
        android:layout_width="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="10dp"/>

</LinearLayout>
  • 创建一个自定义适配器:
public class FruitAdapter extends ArrayAdapter<Fruit>{
    // ListView 子项布局id
    private 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 = getItem(position);
        View view;
        ViewHolder viewHolder;
        if(convertView == null){
            view = LayoutInflater.from(getContext())
                    .inflate(resourceId, parent, false);
            viewHolder = new ViewHolder();
            viewHolder.fruitImage = view.findViewById(R.id.fruit_image);
            viewHolder.fruitName = view.findViewById(R.id.fruit_name);
            view.setTag(viewHolder);
        }else{
            view = convertView;
            viewHolder = (ViewHolder)view.getTag();
        }
        viewHolder.fruitImage.setImageResource(fruit.getImageId());
        viewHolder.fruitName.setText(fruit.getName());
        return view;
    }
    class ViewHolder{

        ImageView fruitImage;

        TextView fruitName;
    }
}

—— 对 convertView 进行重用,并且使用 ViewHolder 对控件的实例进行缓存,以便提升 ListView 的运行效率

  • 修改 MainActivity 的代码:
public class MainActivity extends AppCompatActivity {
    
    private List<Fruit> fruitList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruit(); // 初始化水果数据
        FruitAdapter adapter = new FruitAdapter(this,R.layout.fruit_item, fruitList);
        ListView listView = findViewById(R.id.list_view);
        listView.setAdapter(adapter);
    }
    private void initFruit(){
        for(int i = 0; i < 2; i++){
            Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
            fruitList.add(apple);
            Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
            fruitList.add(banana);
            // 省略以下重复代码
        }
    }
}
ListView 的点击事件:
  • 参数
  1. parent 用于识别是哪个 ListView
  2. view 是当前 ListView 的 item 的布局,可以使用这个 view,获取里面的控件 id 后操作控件
  3. position 是当前 item 在 fruitList 中的位置
  4. id 是当前 item 在 ListView 里的第几行的位置
ListView listView = findViewById(R.id.list_view);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent,View view,int position,long id) {
        Fruit fruit = fruitList.get(position);
        Toast.makeText(MainActivity.this,
                fruit.getName(),Toast.LENGTH_SHORT).show();
    }
});
步骤:
  • 创建适配器的适配类,定义子项布局
  • 创建自定义适配器,并提升运行效率
  • 修改 MainActivity 代码,添加点击事件

5. 探究 RecyclerView:

  • 相较于 ListView,RecyclerView 的扩展性更好,可以轻松实现纵向布局和横向布局等,同时还可以轻松实现子项中任意控件或布局的点击事件,优化了 ListView 的不足之处,代码的语义也更加清晰
基本用法:
  • app/build.gradledependencies 中添加依赖库
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:26.1.0'
    implementation 'com.android.support:recyclerview-v7:26.1.0'
    implementation 'com.android.support.constraint:constraint-layout:1.1.2'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}
  • activity_main.xml 中添加控件,由于 RecyclerView 并不是内置在系统 SDK 中,所以需要写完整包路径
<android.support.v7.widget.RecyclerView
    android:id="@+id/recycler_view"
    android:layout_height="match_parent"
    android:layout_width="match_parent"/>
  • 定义一个和 ListView 中同样的实体类和子项布局,此处略过
  • 自定义一个适配器,继承自 RecyclerView.Adapter,并将泛型指定为 FruitAdapter.ViewHolderViewHolder 是定义在 FruitAdapter 中的内部类
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{

    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder{

        ImageView fruitImage;
        TextView fruitName;

        /**
         * @param view View 参数通常是 RecyclerView 子项的最外层布局,
         *             因此可以通过 findViewById() 来获取布局内的控件实例。
         */
        ViewHolder(View view){
            super(view);
            fruitImage = view.findViewById(R.id.fruit_image);
            fruitName = view.findViewById(R.id.fruit_name);
        }
    }

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

    /**
     * 此方法用于创建 ViewHolder 实例,在方法中将 fruit_item 布局加载出来,
     * 然后创建一个 ViewHolder 实例,并把加载出来的布局传入到构造函数中,
     * 最后将 ViewHolder 实例返回。
     *
     * @param parent 将新视图绑定到适配器位置后添加到其中的父布局,
     *               在这里指 RecyclerView 的布局。
     * @param viewType 新视图的视图类型,可以通过此参数扩展
     *                 不同类型的子项视图。
     */
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
        
        // 为子项加载传入的 R.layout.fruit_item 这个子项布局
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.fruit_item, parent, false);
        return new ViewHolder(view);
    }

    /**
     * 此方法用于对子项的数据进行赋值,会在每个子项被滚动到屏幕内
     * 的时候执行,这里通过 position 参数得到当前项的 Fruit 实例,
     * 然后再将数据设置到 ViewHolder 的 ImageView 和 TextView 当中。
     *
     * @param holder 由 onCreateViewHolder() 方法中传来的返回值
     * @param position 子项在适配器中的位置
     */
    @Override
    public void onBindViewHolder(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();
    }
}
  • 修改 MainActivity 中的代码,在获取布局管理器之后可以调用布局管理器的方法 setOrientation() 来改变方向排列,传入 LinearLayoutManager.HORIZONTAL 即可修改为水平方向排列
public class MainActivity extends AppCompatActivity {

    private List<Fruit> fruitList = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initFruits();// 初始化水果数据
        RecyclerView recyclerView = findViewById(R.id.recycler_view);

        /**
         * 使用 LayoutManager 来指定 RecyclerView 的布局方式,这里使用线性布局,
         * 可以实现和 ListView 一样的效果。
         */
        LinearLayoutManager manager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(manager);
        FruitAdapter adapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }
    private void initFruits(){
        for(int i = 0; i < 100; i++){
            Fruit apple = new Fruit("Apple", R.drawable.apple_pic);
            fruitList.add(apple);
            Fruit orange = new Fruit("Orange", R.drawable.orange_pic);
            fruitList.add(orange);
            Fruit banana = new Fruit("Banana", R.drawable.banana_pic);
            fruitList.add(banana);
            Fruit lemon = new Fruit("Lemon", R.drawable.lemon_pic);
            fruitList.add(lemon);
            Fruit strawberry = new Fruit("Strawberry", R.drawable.sstrawberry_pic);
            fruitList.add(strawberry);
        }
    }
}
  • 网格布局 & 瀑布流布局

GridLayoutManager manager = new GridLayoutManager();
(Context context, int spanCount);

—— 网格布局

StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager();
(int spanCount, int orientation);

—— 瀑布流布局

点击事件
  • 修改 FruitAdapter.java 即可
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{

    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder{

        View fruitView;
        ImageView fruitImage;
        TextView fruitName;

        /**
         * @param view View 参数通常是 RecyclerView 子项的最外层布局,
         *             因此可以通过 findViewById() 来获取布局内的控件实例。
         */
        ViewHolder(View view){
            super(view);
            fruitView = view;
            fruitImage = view.findViewById(R.id.fruit_image);
            fruitName = view.findViewById(R.id.fruit_name);
        }
    }

    /**
     * 此方法用于创建 ViewHolder 实例,在方法中将 fruit_item 布局加载出来,
     * 然后创建一个 ViewHolder 实例,并把加载出来的布局传入到构造函数中,
     * 最后将 ViewHolder 实例返回。
     *
     * @param parent 将新视图绑定到适配器位置后添加到其中的父布局,
     *               在这里指 RecyclerView 的布局。
     * @param viewType 新视图的视图类型,可以通过此参数扩展
     *                 不同类型的子项视图。
     */
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
        // 为子项加载传入的 R.layout.fruit_item 这个子项布局
        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(),"You clicked view " +
                        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(),"You clicked image " +
                        fruit.getName(), Toast.LENGTH_SHORT).show();
            }
        });
        return holder;
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,189评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,577评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,857评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,703评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,705评论 5 366
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,620评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,995评论 3 396
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,656评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,898评论 1 298
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,639评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,720评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,395评论 4 319
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,982评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,953评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,195评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 44,907评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,472评论 2 342

推荐阅读更多精彩内容