第二行代码第三章笔记

UI开发的点点滴滴

如何编写程序界面

Android中有多种编写程序界面的方式可供选择。Android Studio和 Eclipse总都提供了相应的可视化编辑器,允许使用拖放控件的方式来编写布局,并能在试图上直接修改控件的属性

缺点:可视化编辑工具不利于真正了解界面背后的实现原理,通过这种方式制作出的界面通常不具有很好的屏幕适配性,而且当需要编写较复杂的界面时,可视化编辑工具将很难胜任

常见控件的使用方法

TextView

match_parent:让当前控件的大小和福布局的大小一样,也就是由父布局界定当前控件的大小

wrap_content:让当前控件的大小能够刚好包含住里面的内容,也就是由控件内容决定当前控件的大小

android:gravity:可选值有top、bottom、left、right、center等,可以用”|”来同时制定多个值,制定center效果等同于center_vertical|center_horizontal,表示文字在垂直和水平方向都居中

android中字体大小可以使用sp作为单位

Button

android:textAllCaps=”false” 禁用大小写转换

EditText

android:hint属性指定了一段提示性的文本

android:maxLines=”2”指定了EditText的最大行数为两行,这样当输入的内容超过两行时,文本就会向上滚动,而EditText则不会再继续拉伸

ProgressBar
style="?anddroid:attr/progressBarStyleHorizontal"
style="@anddroid:style/Widget.progressBar.Horizontal"

使用@表示侍弄固定的style,而不会跟随Theme改变,这style可以在对应的style.xml中找到。而?表示从theme中查找引用的资源名

visable表示控件是可见的,这个值是默认的

不指定android:visiblity时,控件都是可见的。invisable表示控件不可见,但是它仍然占据着原来的位置和大小,可以理解成控件变成透明状态了。gone则表示控件不仅不可见,而且不再战虎任何屏幕空间

我们还可以通过代码来设置控件的可见性,使用的是setVisibility()方法,可以传入View.VISIBLE、View.INVISIBLE和View.GONE 这3种值

通过android:max属性给进度条设置一个最大值,然后在代码中动态地更改进度条的进度

AlertDialog

AlertDialog可以在当前的界面弹出一个对话框,这个对话框是置顶于所有界面元素之上的,能够屏蔽掉其他控件的交互能力,因此AlertDialog一般都是用于提示一些非常重要的内容或者警告信息。比如为了防止用户误删重要内容,在删除前弹出一个确认对话框

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    Button button = (Button) findViewById(R.id.button);
    button.setOnClickListener(this);
}

@Override
public void onClick(View view) {
    switch (view.getId()) {
        case R.id.button:
            AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
            dialog.setTitle("This is Dialog");
            dialog.setMessage("Something important.");
            dialog.setCancelable(false);
            //确认
            dialog.setPositiveButton("ok", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {

                }
            });

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

                }
            });
            dialog.show();
            break;

        default:
            break;
    }
}
ProgressDialog

ProgressDialog和AlertDialog优点类似,都可以在界面上弹出一个对话框,都能够屏蔽掉其他空间的交互能力。不同的是,ProgressDialog会在对话框中显示一个进度条,一般用于表示当前操作比较耗时,让用户耐心等待

@Override
public void onClick(View view) {
    switch (view.getId()) {
        case R.id.button:
            ProgressDialog progressDialog = new ProgressDialog(MainActivity.this);
            progressDialog.setTitle("This is ProgressDialog");
            progressDialog.setMessage("loading...");
            progressDialog.setCancelable(true);
            progressDialog.show();
            break;

        default:
            break;
    }
}

先构建出一个ProgressDialog对象,然后同样可以设置标题、内容、可否取消等属性,最后也是通过show()方法将ProgressDialog显示出来

注意:如果在setCancelable()中传入了false,表示ProgressDialog是不能通过back键取消的,当数据加载完成后必须要调用ProgressDialog的dismiss()方法来关闭对话框,否则ProgressDialog将会一直存在

详解四种基本布局

线性布局

将其所包含的控件在现行方向上依次排列

LinearLayout的排列方向是horizontal,内部的控件就绝对不能将宽度指定为match_parent,因为这样的话,单独的一个控件会将整个水平方向占满,其他的空间就没有可放置的位置了

android:layout_gravity用于指定控件在布局中国的对齐方式

LinearLayout的排列方向是horizontal时,只有垂直方向上的对齐方式才会生效,因为此时水平方向上的长度是不固定的,每添加一个控件,水平方向上的长度都会改变因而无法指定该方向上的对其方式;同样当LinearLayout的排列方向是vertical时只有水平方向上的对其方式才会生效
android:layout_weight属性允许我们使用比例的方式来指定控件的大小,它在手机屏幕的适配性方面可以起到非常重要的作用

dp是android中用于指定控件大小、属性的单位

系统会把LinearLayout下所有控件指定的layout_weight值相加,得到一个总值,然后每个控件所占大小的比例就是该控件的layout_weight值除以刚才算出的总和

相对布局

RelativeLayout又称作相对布局,也是一种非常常用的布局

android:layout_above属性可以让一个控件位于另一个控件的上方
layout_alignLeft表示让控件的左边缘和另一个控件的左边缘对齐

帧布局

FrameLayout又称作帧布局,这种布局没有方便的定位方式,所有的控件都会默认八方在布局的左上角

百分比布局

提供了PercentFrameLayout和PercentRelativelayout这两个全新的布局

android团队将百分比布局定义在support库当中,我们只需要在项目的build.gradle中添加百分比布局库的依赖,就能保证百分比布局在android所有系统版本上的兼容性了

PercentFrameLayout还是会继承FrameLayout的特性,即所有的控件默认都是摆放在布局的左上角

创建自定义控件

所有控件都是直接或间接继承自view的,所欧阳的所有布局都是直接或间接继承自ViewGroup的。View是Android中最基本的一种UI 组件,它可以在屏幕上绘制一块矩形区域,并能响应这块区域的各种事件,因此,我们使用的各种控件其实就是在View的基础之上又添加了各自特有的功能。而ViewGroup则是一种特殊的View,它可以包含很多子View和子ViewGroup,是一个用于放置控件和布局的容器

引入布局

创建title.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="wrap_content"
android:background="@drawable/title_bg">

<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:background="@drawable/back_bg"
    android:text="back"
    android:textColor="#fff"
    />

<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="Title Text"
    android:textColor="#fff"
    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:background="@drawable/edit_bg"
    android:text="edit"
    android:textColor="#fff"
    />

</LinearLayout>

在activity_main.xml中

<?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/activity_main"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.demo.uicustomviews.MainActivity">

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

</LinearLayout>

在MainActivity中将系统自带的标题栏隐藏掉
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //隐藏系统自带的标题栏
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null) {
            actionBar.hide();
        }
    }
}
创建自定义控件

新建TitleLayout继承自LinearLayout,让它成为我们自定义的标题栏控件

public class TitleLayout extends LinearLayout {
    public TitleLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        LayoutInflater.from(context).inflate(R.layout.title , this);
    }
}

通过LayoutInflater的from()方法可以构建一个LayoutInflater对象,然后调用inflate()方法就可以动态加载一个布局文件,inflate()方法接收两个参数,第一个参数是要加载的布局文件的id,这里我们传入R.layout.title,第二个参数是给加载好的布局再添加一个父布局,这里我们想要指定为TitleLayout,于是直接传入this

在布局文件中添加这个自定义控件,修改activity_main.xml中的代码,如下:

 <com.demo.uicustomviews.TitleLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    />

在标题栏中的按钮注册点击事件,修改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() , "You clicked Edit button" , Toast.LENGTH_SHORT).show();
            }
        });
    }
}
RecyclerView

可以轻松实现了和listView同样的效果,还优化了ListView中存在的各种不足之处

为RecyclerView准备一个适配器,新建一个FruitAdapter类,让这个适配器继承自RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder。

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

    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder {
        ImageView fruitImage;
        TextView fruitName;

        public ViewHolder (View view) {
            super(view);
            fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
            fruitName = (TextView) view.findViewById(R.id.fruit_name);

        }
    }

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

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

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

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

}

首先定义了一个内部类ViewHolder,ViewHolder要继承自RecyclerView.ViewHolder。然后ViewHolder的构造函数中要传入一个view参数,这个参数通常就是RecyclerView子项的最外层布局,那么我们就可以通过findViewById()来获取布局中的 ImageView和TextView的实例了

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 = (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 < 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);
            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);
        }
    }
}

首先初始化所有的水果数据,接着在onCreate()方法中我们先获取到RecyclerView的实例,然后创建一个LinearLayoutManager对象,并将它设置到RecyclerView当中。layoutManager用于指定recyclerView的布局方式,这里使用LinearLayoutManager时线性布局的意思,可以实现和listView类似的效果。是接下来创建FruitAdapter的实例,并将水果数据传入到FruitAdapter的构造函数中,最后调用RecyclerView的setAdapter()方法来完成适配器设置,这样RecyclerView和数据支架的关联就建立完成了

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

修改fruit_item.xml布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="100dp"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <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>

在MainActivity中加入一行代码
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
用LinearLayoutManager的setOrientation()方法来设置布局的排列方向,默认是纵向排列的,传入LinearLayoutManager.HORIZONTAL表示让布局横向排列

ListView和RecyclerView的区别:
ListView的布局排列是由自身去管理的,而RecyclerView则将这个工作交给 LayoutManager ,LayoutManager中指定了一套可扩展的布局排列接口,子类只要按照接口的规范来实现,就能定制出各种不同排列方式的布局

除此之外,RecyclerView还给我们提供了GridLayoutManager和StaggredGridLayoutManager这两种内置的布局方式。GridLayoutManager可以用于实现网格布局,StaggredGridLayoutManager可以用于实现瀑布流布局

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 = (RecyclerView) findViewById(R.id.recycler_view);

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

        recyclerView.setLayoutManager(layoutManager);
        FruitAdapter adapter = new FruitAdapter(fruitList);
        recyclerView.setAdapter(adapter);
    }

    /**
     * 初始化水果数据
     */
    private void initFruits() {
        for (int i = 0; i < 2; i++) {
            Fruit apple = new Fruit(getRandomLengthName("Apple"), R.drawable.apple_pic);
            fruitList.add(apple);
            Fruit banana = new Fruit(getRandomLengthName("Banana"), R.drawable.banana_pic);
            fruitList.add(banana);
            Fruit orange = new Fruit(getRandomLengthName("Orange"), R.drawable.orange_pic);
            fruitList.add(orange);
            Fruit watermelon = new Fruit(getRandomLengthName("Watermelon"), R.drawable.watermelon_pic);
            fruitList.add(watermelon);
            Fruit pear = new Fruit(getRandomLengthName("Pear"), R.drawable.pear_pic);
            fruitList.add(pear);
            Fruit grape = new Fruit(getRandomLengthName("Grape"), R.drawable.grape_pic);
            fruitList.add(grape);
            Fruit pineapple = new Fruit(getRandomLengthName("Pineapple"), R.drawable.pineapple_pic);
            fruitList.add(pineapple);
            Fruit strawberry = new Fruit(getRandomLengthName("Strawberry"), R.drawable.strawberry_pic);
            fruitList.add(strawberry);
            Fruit cherry = new Fruit(getRandomLengthName("Cherry"), R.drawable.cherry_pic);
            fruitList.add(cherry);
            Fruit mango = new Fruit(getRandomLengthName("Mango"), R.drawable.mango_pic);
            fruitList.add(mango);
        }

    }

    private String getRandomLengthName(String name) {
        Random random = new Random();
        int length = random.nextInt(20) + 1;
        StringBuilder builder = new StringBuilder();
        for (int i = 0 ; i < length ; i ++) {
            builder.append(name);
        }
        return builder.toString();
    }

}
RecyclerView的点击事件

修改FruitAdapter中的代码

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

    private List<Fruit> mFruitList;

    static class ViewHolder extends RecyclerView.ViewHolder {
        View fruitView;
        ImageView fruitImage;
        TextView fruitName;

        public ViewHolder (View view) {
            super(view);
            fruitView = view;
            fruitImage = (ImageView) view.findViewById(R.id.fruit_image);
            fruitName = (TextView) view.findViewById(R.id.fruit_name);

        }
    }

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

    @Override
    public ViewHolder onCreateViewHolder(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 view) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(view.getContext() , "you clicked view" + fruit.getName() , Toast.LENGTH_SHORT).show();
            }
        });

        //实现子项中ImageView的点击事件
        holder.fruitImage.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                int position = holder.getAdapterPosition();
                Fruit fruit = mFruitList.get(position);
                Toast.makeText(view.getContext() , "you clicked image" + fruit.getName() , Toast.LENGTH_SHORT).show();
            }
        });

        return holder;
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Fruit fruit = mFruitList.get(position);
        holder.fruitImage.setImageResource(fruit.getImageId());
        holder.fruitName.setText(fruit.getName());
    }

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

}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值