UI

常用控件

1:TextView

android:gravity="center",指定文字的对齐方式为位于控件内部的中部,即水平居中和垂直居中

2:Button

android:textAllCaps="false"禁止自动大写字母转换

为Button的点击事件注册一个监听器
注册匿名类方式

button.setOnClickListener(new View.OnClickListener(){
    @Onverride
    public void onClick(View v){
        //添加处理逻辑
    }
}

通过实现接口的方式
类实现View.OnCLickListener接口
给按钮注册监听器:

button.setOnCLickListener(this)
    @Onverride
    public void onClick(View v){
        switch(v.getId(){
            case R.id.button:
                //添加处理逻辑
                break;
            default:
        }
    }

3: EditText

android:hint属性指定提示性的文本
android:maxLines="2"指定最大行数为2行,文本超过时会自动滚动
获取到EditText中的内容:

private EditText editText;
editText = (EditText) findViewByid
String text = editText.geetText().toString();

4:ImageView

android:src="@drawable/img_1"

动态更改图片:

imageView.setImageResource(R.drawable.img_2);

5:ProgressBar

android:visibilitty="visible/invisible/gone"控制控件的可见性
代码中

progress.setVisible(View.VISIBLE)
style="?android:attr/progressBarStyleHorizontal"

android:max="100"圆形进度条

动态更改进度条的进度
int progress = progressBar.getProgress();
progress +=10;
progressBar.setProgress(progress);

6:AlertDialog

AlertDialog.Builder dialog = new AlertDialog.Builder(MainActivity.this);
dialog.setTitle("Dialog Title");
dialog.setMessage("something important");
dialog.setCancelable(false);
dialog.setPositviButton("OK",new DialogInterface.OnClickListener(){
    @Onverride
    {
        public void onClick(){DialogInterface dialog, int which){
        }
    };
dialog.setNegativeButton("Cancel",new DialogInterface.OnClickListener(){
        @Onverride
        public void onClick(DialogInterface dialog, int which){
        }
    }
}
dialog.show();

7:ProgressDialog

ProgressDialog progressDialog = new ProgressDialog(MainActivity.his);
progressDialog.setTitle("ProgressDialog");
porgressDialog.setMessage("Loaing...");
progressDialog.setCancelable(true);
progressDialog.show();

当setCancelable中传入false,表示不能通过Back键来取消的,当数据加载完成后必须要调用ProgressDialog.dismiss来关闭对话框

8 四种布局

线性布局:android:orientation属性指定了排列方向,可选vertical,默认为horizontal
android:layout_gravity属性用于指定控件在布局中的对其位置。注意在指定了某种排列方式后,如水平排列,那么只有垂直方向的对齐方式才会有效。

可以使用androdi:layout_weight按比例排列

<EidtText
android:layout_weight="1"
android:layout_width="0" />
<Button
android:layout_width="wrap"/>

指定部分控件的layout_weight

相对布局

RelativeLayout通过相对定位的方式控制控件在布局中的位置

android:layout_alingParentLeft|Rihgt|Top|Bottom="true"
android:layout_centerInParent="true"

每个控件都是相对父布局进行定位,也可以相对于控件定位

android:layout_above="@id/button3"
android:layout_toLeftOf="@id/button3"

帧布局

所有控件默认都位于布局的左上角,后添加的控件会遮挡之前定义的控件
使用layout_gravity属性指定控件在布局中的对齐方式,类似LinearLayout

百分比布局

buidl.gradle中添加布局库的依赖

compile 'com.android.support:percent:25.0.0"


定义一个app的命名空间
包括PercentFrameLayout和PercentRelativeLayout
通过layout_layoutheightPercent控制height和width的百分比
通过layout_gravity控制对齐方式

自定义控件

1.引入布局
    实现一个自定义标题栏。标题栏布局,然后把标题栏布局引入。如
    title.xml

        <LinearLayout
        ...>
        <Button
        .../>
        <EditText
        .../>
        <Button
        .../>
        </LinearLayout>
    activity_main.xml中引入标题栏布局

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

        ActionBar actionBar = getSupportActionBar();
        if (actionBar!=null)
            actionBar.hide();

缺点:布局中的控件如果需要响应事件,必须在每个活动中都要编写事件注册的代码。

2.创建自定义控件

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

通过LayoutInflater的from方法构建LayoutInflater对象,调用infalte方法动态加载一个布局文件

在布局文件中添加自定义控件

<com.example.uitest.TitleLayout
android:layot_width...
.../>

为标题栏中按钮注册点击事件

public class TitleLayout...{
    ...
    Button back  = (Button) findViewById(R.id.back);
    Button edit = (Button) findViewById(R.id.edit);
    back.setOnClickListener(new OnClickListener(){
        @Onverride
        public void onClick(View v){
            ((Activity)getContext()).finish();
        }
    });
    edit.setOnClickListener(new OnClickListener(){
        @Onverride
        public void onClick(View v){
            //添加处理逻辑
        }
    });
}

9:ListView

布局中添加ListView控件

<LinearLayout
...>
    <ListView
        android:id..
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

不能直接将大量数据传给ListView显示,需要借助适配器。 比如ArrayAdapter,通过泛型来指定适配的数据类型, 然后在构造函数中将要适配的数据传入。

ArrayAdatpter的构造函数中依次传入当前上下文、ListView子项布局的id、以及要适配的数据。 此处使用 android.R.layout.simple_list_item_1作为ListView子项布局的id,作为Android内置的布局文件,只有一个TextView,简单显示一段文本

private String[] data={...};//数据
...
ArrayAdapter<String> adapter = new ArrayAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, data);//若需要改变子项布局,新建ListView子项布局文件
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);//将适配器对象传递,使ListView和数据之间建立关联

定制ListView界面

ListView子项抽象为一个对象,即需要新建一个实体类,作为ListView适配器的适配类型,如同前面的Strinig类型对象。

public class Fruit{
        private String name;
        private int imageId;
        ...
    }

为ListView子项新建一个布局文件fruit_item.xml,包括水果图片和水果名称

<LinearLayout
... >
    <ImageView
        android:id="@+id/fruit_image"
        .. />
    <TextView
        android:id="@+id/fruit_name"
        ... />
</LinearLayout>

新建一个继承ArrayAdapter的自定义适配器FruitAdapter,泛型指定为前面建立的实体类Fruit

public class FruitAdapter extends ArrayAdapter<Fruit>{
    private int resourceId;
    public FruitAdapter(Context context, int textViewResourceId, List<Fruit> objects){
        super...;//上下文,子项布局id和数据传递进来
        resourceId = textViewResourceId;
        }
    @Override
    public View getView(int position, View convertView, ViewGroup parent){
        Fruit fruit = getItem(position);//获取当前项的Fruit实例
        View view = LayoutInflater.from(getContext()).infalte(resourceId, parent, false);
        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;
}

为每个进入到屏幕内的子项获取Fruit实例,加载子项布局,并通过Fruit实例的数据设置子项布局内控件即图片和文字的值。 在活动中创建FruitAdapter适配器的实例,并为ListView设置适配器

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

FruitAdapter adpater = new FruitAdpater(MainActivity.this, R.layout.fruit_item, fruitList);
ListView listView = (ListView) findViewById(R.id.list_view);
listView.setAdapter(adapter);
...

优化ListView缓存子项布局和缓存控件的实例

View view;
if (convertView == null){
    view = LayoutInflater.from(getContext()).infalte(resourceId, parent, false);
}else{
    view = currentView;
    }

新建一个内部类ViewHolder,对控件实例缓存

 @Override
public View getView(int position, View convertView, ViewGroup parent) {
    Fruit fruit = getItem(position); // 获取当前项的Fruit实例
    View view;
    ViewHolder viewHolder;
    if (convertView == null) {
        view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
        viewHolder = new ViewHolder();
        viewHolder.fruitImage = (ImageView) view.findViewById (R.id.fruit_image);
        viewHolder.fruitName = (TextView) view.findViewById (R.id.fruit_name);
        view.setTag(viewHolder); // 将ViewHolder存储在View中
    } else {
        view = convertView;
        viewHolder = (ViewHolder) view.getTag(); // 重新获取ViewHolder
    }
    viewHolder.fruitImage.setImageResource(fruit.getImageId());
    viewHolder.fruitName.setText(fruit.getName());
    return view;
}

class ViewHolder {

    ImageView fruitImage;

    TextView fruitName;

}

ListView点击事件

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

为ListView注册一个监听器,点击某个子项时,回调onItemClick方法,判断点击的是哪个子项

缺点:不能实现横向滚动,只能纵向滚动

10:RecyclerView 需要在app/build.gradle添加依赖库

compile 'com.android.support:recyclerview-v25.0.0'

在布局文件中添加RecyclerView控件

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

与ListView一样,需要实体类Fruit和子项布局文件fruit_item.xml。稍微不同的是适配器FruitAdapter,继承RecyclerView.Adapter,泛型指定为FruitAdapter.ViewHolder,ViewHolder是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 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;
    }

 @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,View和ListView相同用法 ListView的getView方法可以理解为拆分为OnCreateViewHolder和onBindViewHolder,获取子项布局,以及ViewHolder对象,利用ViewHolder对象对控件的值进行控制,最后还重写了getItemCount方法。 在活动中的代码和ListView差不多

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

多了XXXXXLayoutManager用于指定RecyclerView的布局方式,比如LinearLayoutManager表示线性布局,效果类似ListView。还可以实现横向滚动,设置layoutManager的布局排列方向属性

layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);

GridLayoutManager和StaggeredGridLayoutManager分别可以实现网格布局和瀑布流布局

StaggeredGridLayoutManager layoutManager = new
            StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
    recyclerView.setLayoutManager(layoutManager);//列数和布局排列方式2个参数

RecyclerView的点击事件

不同于ListView的setOnItemClickListener注册监听器方法,RecyclerView必须要自己给子项具体的View注册点击事件 如前面FruitAdapter中的代码,ViewHolder不仅有子项布局中的控件缓存还保存了子项最外层布局实例,在onCreateViewHolder中注册点击事件,分别为最外层布局和ImageView注册了点击事件。为子项布局中的任意控件或布局注册点击事件。