1. 程式人生 > >2、Android-UI(自定義控件&ListView)

2、Android-UI(自定義控件&ListView)

dap mat 重載 group 引入 log match 出現 androi

2.4、系統控件不夠用創建自定義控件

控件的和布局的集成結構:

技術分享圖片

所有的控件都是間接或者直接集成View的

所有的布局都是直接或者間接繼承自ViewGroup的

View是Android種最基本的一種UI組件

可以再屏幕上進行創建任何布局或者各種事件

所以使用的各種控件其實就是再View的基礎上添加了特有的功能

ViewGroup是一個特殊的View

他可以包含很對的子View和ViewGroup,是一個用於放置控件和布局的容器

當系統提供的控件不滿足開發時,可以自己創建自定義的控件

1、引入布局

實現標題欄的代碼進行解析

新建一個tittle.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">

<Button
    android:id="@+id/title_back"
    android:layout_width="wrap_content"
    android:layout_height
="wrap_content" android:layout_margin="5dp" android:text="back" android:textColor="#fff" android:background="#90a" /> <TextView android:id="@+id/title_text" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight
="1" android:gravity="center" android:text="Title message" android:textColor="#90a" android:textSize="24sp" /> <Button android:id="@+id/title_edit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_margin="5dp" android:text="Edit" android:textColor="#fff" android:background="#90a" /> </LinearLayout>

這裏使用android:background用於只當控件的背景(可以是圖片可以是顏色)

android:layout_margin:用於指定控件再上下左右方向的便宜距離

效果:

技術分享圖片

再first_layout種進行使用這個標題:

此時直接使用<include>這個標簽進行引用

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

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

</LinearLayout>

技術分享圖片

同時還需要將系統自帶的標題欄給隱藏

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Log.d("FirstActivity====", String.valueOf(getTaskId()));
        setContentView(R.layout.first_layout);

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

    }

使用getSupportActionBar()方法獲得ActionBar的實例

再調用hide()方法進行隱藏

2、創建自定義的控件

引入布局的技巧解決了重復編寫代碼的問題

但是一個布局種有一些控件要求能夠響應事件

還需要再每個活動中為這些控件單獨的編寫註冊的代碼

測試標題欄控件的實現:

新建類繼承LinerLayout

public class Tittle extends LinearLayout {

    public Tittle(Context context, AttributeSet attrs) {
        super(context, attrs);
        LayoutInflater.from(context).inflate(R.layout.tittle,this);

        Button buttonBack = (Button) findViewById(R.id.title_back);
        Button buttonEdit = (Button) findViewById(R.id.title_edit);

        buttonBack.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //退出
                ((Activity)getContext()).finish();
            }
        });
        buttonEdit.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(getContext(),"you click EDIT Button",Toast.LENGTH_LONG).show();
            }
        });
    }
}

重寫LinearLayout構造函數

在布局引入Tittle時就會調用這個函數

在構造函數中對標題欄進行動態加載

通過LayoutInflater的from()方法可以構建處一個LayoutInflater對象

然後調用inflate()方法可以動態加載一個布局文件,兩個參數:

1、要加載布局文件的id

2、加載好布局在添加一個父布局

然後再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">

<com.example.ccrr.myapplication.Tittle
android:layout_width="match_parent"
android:layout_height="wrap_content">
</com.example.ccrr.myapplication.Tittle>
</LinearLayout>

需要指明控件的完整類名

點擊BACK時退出

點擊EDIT時出現提示信息

技術分享圖片

2.5、ListView

是Android中最長使用的控件之一

由於手機的屏幕有限,顯示的數據內容不多,當有大量的數據需要展示的時候

可以使用ListView進行實現

類似QQ的好友列表...

1、簡單實現

在first_layout中:

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

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

簡單的加入該控件

MainActivityz中:

public class MainActivity extends AppCompatActivity {

    private String [] data = {"apple","Banana","Orange","Water",
    "pear","Grape","pineapple","strawberry","cerry","Mango",
           "1","2","3","4","5","6","7"
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.firstlayout);
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null){
            actionBar.hide();
        }

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

    }
}

使用數組提供數據進行顯示

使用適配器ArrayAdapter來實現(指定類型)

有多個構造函數的重載

此時的參數:

1、當前的上下文

2、ListView的子布局id,這是Android內嵌的布局文件

3、數據

最後使用ListView的setAdapter()方法將構建好的適配器對象傳進去

此時數據和ListView之間就建立了聯系

技術分享圖片

2、定制界面

實現圖片加數據的顯示

定義一個實體類作為ListView的適配類型

public class Fruit {
    //name:說過名字
    private String name;
    //商品的圖片id位置
    private int id;
    public Fruit(String name, int id) {
        this.name = name;
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
}

新建:

技術分享圖片

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

    <ImageView
        android:src="@drawable/qq"
        android:id="@+id/image_view"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/text_view"
        android:layout_marginLeft="10dp"
        android:layout_gravity="center_vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>

使用ImageView用於保存圖片

使用TextView用於顯示水果的名稱

自定義一個適配器繼承ArrayAdapter,並將泛型指定為Fruit類

public class FruitAdapter extends ArrayAdapter<Fruit> {

    private int resourceId;

    public FruitAdapter(Context context,  int textViewResourceId, List<Fruit> objects) {
        super(context, textViewResourceId, objects);
        resourceId=textViewResourceId;
    }

    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        Fruit fruit = getItem(position);//獲取當前項Fruit實例
        View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);

        ImageView imageView = (ImageView) view.findViewById(R.id.image_view);
        TextView textView = (TextView) view.findViewById(R.id.text_view);

        imageView.setImageResource(fruit.getId());
        textView.setText(fruit.getName());

        return view;
    }
}

重寫父類的一組構造函數,用於將上下文、ListView子項布局的id和數據都傳進來

由重寫geiView()方法,將每個子項被滾動到屏幕內的時候會被調用

在方法中首先得到當前的Fruit的實例,然後再LayoutInflater來為這個子項目加載到我們傳入的布局

第三個參數:指定為false表示只讓我們在父布局中聲明的layout屬性生效,但不為這個View添加父布局

因為一旦有了父布局之後,他就不能再添加到ListView中。

再View中findViewById()方法分別用於獲取ImageView和TextView的實例,並且調用他們的

setImageResource()和setText()方法來設置圖片和文字

此時自定義的適配器就完成了

再MianActivity中

public class MainActivity extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<>();

    private void initFruit(){
        for (int i =0;i<6;i++){
            Fruit apple = new Fruit("Apple1",R.drawable.qq);
            fruitList.add(apple);

            Fruit apple1 = new Fruit("Apple2",R.drawable.qq);
            fruitList.add(apple1);

            Fruit apple2 = new Fruit("Apple3",R.drawable.qq);
            fruitList.add(apple2);

            Fruit apple3 = new Fruit("Apple4",R.drawable.qq);
            fruitList.add(apple3);

            Fruit apple4 = new Fruit("Apple5",R.drawable.qq);
            fruitList.add(apple4);

            Fruit apple5 = new Fruit("Apple6",R.drawable.qq);
            fruitList.add(apple5);

            Fruit apple6 = new Fruit("Apple7",R.drawable.qq);
            fruitList.add(apple6);
        }

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.firstlayout);
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null){
            actionBar.hide();
        }

        initFruit();

        FruitAdapter adapter = new FruitAdapter(
                MainActivity.this,R.layout.fruit_item,fruitList);

        ListView listView = (ListView) findViewById(R.id.list_view);
        listView.setAdapter(adapter);
    }
}

這裏使用initFruit()進行初始化數據,數據可能來源於網絡和數據庫中。

初始化時將圖片和名字傳入到實例中。

然後將適配器傳入ListView中即可進行顯示,

技術分享圖片

此時定制的頁面比較簡單,只要修改對應的文件內容就可以制作出各種復雜的界面。

3、提升ListView的運行效率

對於ListView很難使用的原因時他可以有很多的細節可以進行優化

運行效率就是重要之一

目前使用的ListView的運行效率是比較低的

FruitAdaper的getView()方法中,每次都將布局重新加載一遍

當ListView快速滾動的時候就會成為性能的瓶頸

仔細觀察可以看出getView()方法中還有一個參數convertView

這個參數用於將之前加載好的布局進行緩存,以便重用

package com.example.ccrr.myapplication.empty;

import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.ccrr.myapplication.R;

import java.util.List;

/**
 * Created by ccrr on 2019/4/8.
 */

public class FruitAdapter extends ArrayAdapter<Fruit> {


    private int resourceId;

    public FruitAdapter(Context context,  int textViewResourceId, List<Fruit> objects) {
        super(context, textViewResourceId, objects);
        resourceId=textViewResourceId;
    }

    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        Fruit fruit = getItem(position);//獲取當前項Fruit實例
        //View view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
        View view;
        if (convertView ==null){
            view=LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
        }else {
            view = convertView;
        }

        ImageView imageView = (ImageView) view.findViewById(R.id.image_view);
        TextView textView = (TextView) view.findViewById(R.id.text_view);

        imageView.setImageResource(fruit.getId());
        textView.setText(fruit.getName());

        return view;
    }
}

修改上述的代碼,重新運行!

這裏再getView()中進行判斷

如果為null,則使用LayoutInflater去加載布局

不為null,則直接對convertView進行重用

這就大大的提高了效率

現在的代碼還能進行優化

此時不會再重復去加載布局

但是每次再geiVIew()方法中會調用View的findViewByID()方法來獲取一次控件的實例

此時可以借助ViewHolder對這部分進行優化

package com.example.ccrr.myapplication.empty;

import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.TextView;

import com.example.ccrr.myapplication.R;

import java.util.List;

/**
 * Created by ccrr on 2019/4/8.
 */

public class FruitAdapter extends ArrayAdapter<Fruit> {


    private int resourceId;

    public FruitAdapter(Context context,  int textViewResourceId, List<Fruit> objects) {
        super(context, textViewResourceId, objects);
        resourceId=textViewResourceId;
    }



    @NonNull
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        Fruit fruit = getItem(position);//獲取當前項Fruit實例
        ViewHolder viewHolder;
        View view;

        if (convertView ==null){
            view=LayoutInflater.from(getContext()).inflate(resourceId,parent,false);
            viewHolder = new ViewHolder();
            viewHolder.imageView = (ImageView) view.findViewById(R.id.image_view);
            viewHolder.textView = (TextView) view.findViewById(R.id.text_view);

            view.setTag(viewHolder);
        }else {
            view = convertView;
            viewHolder= (ViewHolder) view.getTag();
        }

        viewHolder.imageView.setImageResource(fruit.getId());
        viewHolder.textView.setText(fruit.getName());

        return view;
    }

    class ViewHolder{
        ImageView imageView;
        TextView textView;
    }
}

新增一個內部類ViewHolder用於對控件進行實例緩存

同理再if中進行判斷

用View的setTag()方法將其存入View中

再有緩存時使用getTag()方法取出

4、ListView的點擊事件

上述的ListView只是視覺效果,並沒有點擊的用途

此時實現其點擊事件

public class MainActivity extends AppCompatActivity {private List<Fruit> fruitList = new ArrayList<>();

    private void initFruit(){
        for (int i =0;i<6;i++){
            Fruit apple = new Fruit("Apple1",R.drawable.qq);
            fruitList.add(apple);

            Fruit apple1 = new Fruit("Apple2",R.drawable.qq);
            fruitList.add(apple1);

            Fruit apple2 = new Fruit("Apple3",R.drawable.qq);
            fruitList.add(apple2);

            Fruit apple3 = new Fruit("Apple4",R.drawable.qq);
            fruitList.add(apple3);

            Fruit apple4 = new Fruit("Apple5",R.drawable.qq);
            fruitList.add(apple4);

            Fruit apple5 = new Fruit("Apple6",R.drawable.qq);
            fruitList.add(apple5);

            Fruit apple6 = new Fruit("Apple7",R.drawable.qq);
            fruitList.add(apple6);
        }

    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.firstlayout);
        ActionBar actionBar = getSupportActionBar();
        if (actionBar != null){
            actionBar.hide();
        }

        initFruit();

        FruitAdapter adapter = new FruitAdapter(
                MainActivity.this,R.layout.fruit_item,fruitList);

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

這裏使用setOnItemClickListener()方法為ListView註冊一個監聽器

當用戶點擊任何一個子項時

就會回調onItemClick()方法

這個方法中通過position參數判定用戶點擊是哪一個子項

然後獲得響應的事件

此時是打印處結果

技術分享圖片

2、Android-UI(自定義控件&ListView)