第二章 布局技巧与列表

1 布局与优化

1.1 五大布局

  • LinearLayout:线性布局
  • orientation:vertical:垂直的,horizontal:水平的
  • weight:设置比重时,layout_width="0"。weightSum总比重。例如:微信底部四个tab平分宽带,可以使其weight 1:1:1:1
  • RelativeLayout:相对布局
  • xmlns:xml的命名空间
  • layout_alignParentRight:与布局容器的右边对齐
  • layout_alignParentBottom:位于父容器的底部
  • layout_alignLeft:与给出id组件的左边对齐
  • layout_above/toRightOf:给出id组件的上方/右边
  • layout_marginTop/toRightOf:外边距
  • layout_paddingRight:内边距
  • FrameLayout:帧布局,一层一层叠加起来
  • AbsoluteLayout:绝对布局
  • TableLayout:表格布局
  • TableRow

1.2 布局优化

  • 减少布局层次的重要性层次过多会影响到性能
  • 官方建议布局层次--最多10层,所以要减少层次,删除无用布局
  • 学会使用相对布局。布局结构要清晰,选择合适的布局
  • 组合控件
  • 一些有用的属性
  • 布局如何引用相同的部分,可以提取出来然后重用布局文件
  • 减少视图层级
  • 需要是才加载,介绍ViewStub代码 ,介绍ViewStub文字,两篇结合来看。ViewStub指向的布局文件在多数情况下并不会被显示出来,只会在某种特殊的情况下对ViewStub进行inflate操作,而且这种操作只能进行一次,此时会将ViewStub替换掉,变成显示所指向的布局文件。,一旦替换后,此时原来的布局文件中就没有ViewStub控件了,这就好像ViewStub只是为其所指向的布局占了一个位子,需要时才显示
  • 小技巧
  • 不要嵌套多个使用layout_weight属性的LinearLayout
  • Android lint :删除无用的资源,布局,类,引用
  • HierarchyViewer:分析性能及其优化

2 无比重要的ListView

第二章 布局技巧与列表_第1张图片
这里写图片描述
  • AdapterView是一个容器,把很多的列表项以合适的形式显示出来,而列表项的内容和格式可以用Adapter指定,调用AdapterView的setAdapter(Adapter)方法设置Adapter即可。

2.1四种设置Adapter的方法:(以ListView为例子)

  • 使用ArrayAdapter设置
    1. 告诉大家我要创建列表项了,初始化这个控件
    2. 定义了一个数组,该数组是为多个列表项提供了数据
    3. 创建ArrayAdapter(Context,textViewResource,数组或者List)对象。其中Context代表访问应用的接口,一般是this。textViewResource代表一个资源的ID,该资源分ID是指定了每个列表项的组件,等于为每个列表项指定了格式。后面的数据就按照组件的格式传入。
    4. 最后为该ListView设置Adapter
public class ArrayAdapterActivity extends Activity{
    ListView mListView1;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_arrayadapter);
        //1
        mListView1 = (ListView) findViewById(R.id.listview1);
        //2
        String[] arr1 = {"王俊凯" ,"王源","易烊千玺"};
        //3
        ArrayAdapter adapter1 =
                new ArrayAdapter(this,R.layout.array_item,arr1);
        //4
        mListView1.setAdapter(adapter1);    
    }
}

  • 让Activity继承ListActivity来实现
  1. 创建ArrayAdapter对象之后,直接使用setListActivity(adapter)设置该窗口显示列表
public class MainActivity extends ListActivity{
    @override
    public void onCreate(Bundle saveIdnstanceState){
        supert.onCreate(savedInstanceState);
        String[] arr = {"刘诗诗", "赵丽颖", "杨幂"};
        ArrayAdapter adapter1 = new ArrayAdapter(this, R.layout.item, arr);
        setListAdapter(adapter);
         
    }
}
  • 使用SimpleAdapter
  1. 前面都是创建提供列表内容的数组
  2. 创建一个List集合,集合元素是Map,把前面的内容数组封装成一个List集合
  3. 创建SimpleAdapter对象。后面有5个参数,第一个是Context。第二个是一个元素是Map的List集合,每个Map对象生成一个列表项。第三个参数是指定界面布局的ID。第四个是一个String[]对象的参数,决定提取Map中那些key对应的value来生成列表项。第五个参数是对应于value填充的组件,该组件是来源与第三个参数的布局。是int[]类型的参数
  4. 设置Adapter
public class MainActivity extends AppCompatActivity {
    //1
    private  String[] names = new String[]{"lisa" , "linda" , "july" , "anna"};

    private String[] describe = new String[]{"a cute child" , "a beautiful girl" , "a smart student" , "a handsome boy"};

    private int[] imageId = new int[]{R.drawable.libai,R.drawable.nongyu,R.drawable.qingzhao,R.drawable.tiger};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //2
        List> listItems = new ArrayList>();

        for (int i = 0 ; i < names.length ; i++){
           Map listItem = new HashMap();
            listItem.put("header" ,imageId[i]);
            listItem.put("name" , names[i]);
            listItem.put("describe" , describe[i]);
            listItems.add(listItem);
        }
        //3
        SimpleAdapter simpleAdapter = new SimpleAdapter(this ,
                listItems ,R.layout.simple_item ,
                new String[]{"header","name","describe"} ,
                new int[]{R.id.header,R.id.name,R.id.desc});

        ListView listView =(ListView) findViewById(R.id.mylist);
        //4
        listView.setAdapter(simpleAdapter);

        Intent intent = getIntent();
        if (intent != null) {
            String string = intent.getStringExtra(SplashActivity.TITLE);
            setTitle(string);
        }
    }
}
  • 扩展BaseAdapter实现 ,需要重写4个方法
  1. int getCount():返回值控制Adapter会有多少个列表项
  2. Object getItem(int i):返回第i处的列表项的内容
  3. getItemId(int i):返回第i除列表项的id
  4. View getView(int i, View view, ViewGroup viewGroup):返回第i除列表项的组件
 BaseAdapter baseAdapter = new BaseAdapter() {
            @Override
            public int getCount() {
                //指定40个选项
                return 40;
            }

            @Override
            public Object getItem(int i) {
                return null;
            }

            @Override
            public long getItemId(int i) {
                return i;
            }

            @Override
            public View getView(int i, View view, ViewGroup viewGroup) {
                LinearLayout linearLayout = new LinearLayout(MainActivity.this);
                linearLayout.setOrientation(LinearLayout.VERTICAL);
                ImageView imageView = new ImageView(MainActivity.this);
                imageView.setImageResource(R.mipmap.ic_launcher);
                TextView text = new TextView(MainActivity.this);
                text.setText("this is" + i + "listview");
                text.setTextSize(20);
                linearLayout.addView(imageView);
                linearLayout.addView(text);
                return linearLayout;
            }
        };

2.2 优化

  • 不必每次都要加载视图,只需要第一次的时候加载,所以可以加个判断
@Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        ViewHolder viewHolder;
        //优化
        if(view == null){
            view = mLayoutInflater.inflate(R.layout.activity_items,null);
            viewHolder = new ViewHolder();
            //获取控件
            viewHolder.phone_name = (TextView) view.findViewById(R.id.item_name);
            viewHolder.phone_age = (TextView) view.findViewById(R.id.item_age);
            viewHolder.phone_image = (ImageView) view.findViewById(R.id.item_image);
            view.setTag(viewHolder);
        }else {
            viewHolder = (ViewHolder) view.getTag();
        }
        //数据绑定,mInfoList是一个类型为用户的List,该用户有Age和Name属性
        viewHolder.phone_name.setText(mInfoList.get(i).getmName());
        viewHolder.phone_age.setText(String.valueOf(mInfoList.get(i).getmAge()));
        viewHolder.phone_image.setImageResource(R.mipmap.ic_launcher);
        return view;
    }
  • 设置类来保存所加载视图的控件
class ViewHolder{
    TextView phone_name;
    TextView phone_age ;
    ImageView phone_image ;
}
  • 所以只有第一次加载的时候,会去加载视图获取控件,当前视图不为空时,只会去获取视图里控件,节约内存。

2.3 item不同怎么办,getItemViewType()

  • 推荐一篇写的好而且我看的懂的博客Adapter的getViewTypeCount()和getItemViewType()
  • 微信的聊天列表
  • 聊天框使用.9图,用getItemViewType()设置不同的ListView
  • 小demo非正式仿聊天界面

2.4 风格不同的分割线

  • ListView的 android:divider属性来设置,所以可以用自定义的Drawable来设置分割线的风格

3 GridView和ScrollView

3.1 GridView与ListView的相似之处

  • 需要设置Adapter
  • 它们俩的parent都实现了AdapterView

3.2 GridView的常用实现

  • 常用属性:
  • android:numColums:设置列数
  • android:horizontalSpacing:设置各元素之间的水平间距
  • android:verticalSpacing:设置各元素之间的垂直间距
  • android:gravity:设置对齐方式
  • 使用了SimpleAdapter,ImageSwitcher是图片切换器,在切换ImgeView时使用动画
  • activity_main.xml



    
    


  • cell.xml



    


  • java源代码
package com.example.w.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.ImageSwitcher;
import android.widget.ImageView;
import android.widget.SimpleAdapter;
import android.widget.Toast;
import android.widget.ViewSwitcher;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class MainActivity extends AppCompatActivity {

    GridView gridView;
    ImageSwitcher imageSwitcher;

    int[] imageIds = new int[] {
            R.drawable.bomb5,R.drawable.bomb6,R.drawable.bomb7,R.drawable.bomb8,
            R.drawable.bomb9,R.drawable.bomb10,R.drawable.bomb11,R.drawable.bomb12,
            R.drawable.bomb13,R.drawable.bomb14,R.drawable.bomb15,R.drawable.bomb16,
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        gridView = (GridView)findViewById(R.id.grid01);

        //创建一个List对象,List对象的元素是Map
        List> listItems = new ArrayList>();
        for (int i = 0 ; i < imageIds.length ; i++){
            Map listItem = new HashMap();
            listItem.put("image" , imageIds[i]);
            listItems.add(listItem);
        }
        //为ImageSwitcher提供一个ViewFactory,由其生成View
        imageSwitcher = (ImageSwitcher)findViewById(R.id.switcher);
        imageSwitcher.setFactory(new ViewSwitcher.ViewFactory() {
            @Override
            public View makeView() {
                //创建ImageView对象
                ImageView imageView = new ImageView(MainActivity.this);
                imageView.setScaleType(ImageView.ScaleType.FIT_CENTER);
                imageView.setLayoutParams(new ImageSwitcher.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT,
                        ViewGroup.LayoutParams.WRAP_CONTENT));
                return imageView;
            }
        });
        SimpleAdapter simpleAdapter = new SimpleAdapter(this , listItems ,R.layout.cell
                ,new String[]{"image"},new int[]{R.id.imageView});

        gridView.setAdapter(simpleAdapter);

        //列表被单击
        gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView adapterView, View view, int i, long l) {
                imageSwitcher.setImageResource(imageIds[i]);
                Toast.makeText(MainActivity.this,"图片" + i +"被点击了" , Toast.LENGTH_LONG).show();
            }
        });

        //列表被选中
        gridView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView adapterView, View view, int i, long l) {
                imageSwitcher.setImageResource(imageIds[i]);
                Toast.makeText(MainActivity.this,"图片" + i +"被选中了" , Toast.LENGTH_LONG).show();
            }

            @Override
            public void onNothingSelected(AdapterView adapterView) {

            }
        });
    }
}

3.3 ScrollView

  • 最多只能包含一个组件,或只支持垂直滚动,若需要水平滚动,则可借助另一个滚动视图-HorizontalScrollView

你可看可不看,但我必须得看的Tips:

  1. ScrolView了解不是很多,需要进一步去了解
  2. ListView很重要,和BaseAdapter搭配起来,天生一对
  3. 布局应快要学完了,16.7.23

你可能感兴趣的:(第二章 布局技巧与列表)