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


    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参数判定用户点击是哪一个子项

    然后获得响应的事件

    此时是打印处结果

  • 相关阅读:
    【做题】提高组过关测试题1
    【做题】arc078_f-Mole and Abandoned Mine——状压dp
    【学习】Hall’s Marriage Theorem
    【做题】arc072_f-Dam——维护下凸包
    一文尽览近似最近邻搜索中的哈希与量化方法
    InsightFace源码以及pre-train模型以及使用
    无法解析的外部符号 jpeg_std_error
    威布尔weibull distribution
    中科院- 生物特征识别概述
    Face-Resources
  • 原文地址:https://www.cnblogs.com/Mrchengs/p/10663900.html
Copyright © 2020-2023  润新知