《第一行代码》第2版思维导图及所有学习笔记目录
本部分思维导图如下:
常用控件 如下总结一些常用的参数配置或者方法,某些参数可能对于其它控件也适用。
TextView 1 2 android:gravity - 指定文字对齐方式,用指定控件内容的位置。 android:layout_gravity - 指定控件相对于父布局的位置。
1 android:textAllCaps="false" - 禁用Button字母自动大写转换
EditText 1 2 android:hint="" android:maxLines=""
ImageView 1 2 android:src="" imageView.setImageResource()
ProgressBar 1 2 3 4 5 6 7 8 9 10 android:visibility="" android:max="" style="?android:attr/progressBarStyleHorizontal" progressBar.getvisibility() progressBar.setVisibility(View.VISIBLE/View.GONE) progressBar.getProgress(); progressBar.setProgress(int);
AlertDialog 在当前界面弹出一个对话框,置于所有界面之上,能屏蔽其他控件的交互能力。
1 2 3 4 5 6 7 8 9 10 11 12 # 弹出一个确认框 AlertDialog.Builder dialog = new AlertDialog .Builder(MainActivity.this ); dialog.setTile("" ); dialog.setMessage("" ); dialog.setCancelable(false ); # 不可通过Back键取消 dialog.setPositiveButton("OK" ,new DialogInterface .onClickListener(){ public void onClick (DialogInterface dialog, int which) {}; }); dialog.setNegativeButton("Cancel" ,new DialogInterface .onClickListener(){ public void onClick (DialogInterface dialog, int which) {}; }); dialog.show();
ProgressDialog 与AlertDialog类似,不过多一个进度条。
1 2 3 4 5 6 ProgressDialog dialog = new ProgressDialog (MainActivity.this );dialog.setTitle("" ); dialog.setMessage("" ); dialog.setCancelable(true ); # 可通过Back键取消 # 如若传入false ,则需要再加载完成后调用ProgressDialog的dismiss()方法关闭对话框,否则对话框会一直存在 dialog.show();
4种基本布局 布局与控件嵌套结构如下:
线性布局 只有LinearLayout支持 android:layout_weight属性,指定了此属性后控件大小即由其来决定,举个简单例子:
1 2 3 4 5 6 7 android:layout_width = "0dp" android:layout_weight = 1 android:layout_width = "0dp" android:layout_weight = 2
百分比布局
前不久好像看到文章讲此布局被弃用了
此布局要使用需先引入依赖库,然后直接指定百分比。
1 2 3 4 5 6 7 8 9 compile 'com.android.support:percent:24.2.1' <android.support.percent.PercentFrameLayout xmlns:app="http://schemas.android.com/apk/res-auto" app:layout_widthPercent="50%" app:layout_heightPercent="50%"
还存在PercentRelativeLayout,除了可以如上使用百分比布局外,继承了RelativeLayout的属性。
自定义控件 所有控件和布局都是直接或间接继承自View,如下:
引入布局 自定义标题栏控件,针对自定义控件新建一布局文件title.xml。然后在主布局文件中引入title.xml。
1 <include layout="@layout/title" />
创建自定义控件 针对布局实现控件响应类。
1 2 3 4 5 6 7 8 9 public class TitleLayout extends LinearLayout { public TitleLayout(Context context, AttributeSet attrs){ super(context,attrs); LayoutInflater.from(context).inflate(R.layout.title,this); // 通过LayoutInflater.from(context)构建LayoutInflater对象,然后inflate()动态加载布局文件 // 参数:布局ID, 父布局 ... //实现控件响应功能 } }
除了通过include引入布局外,亦可以在布局文件中直接添加控件:
1 2 3 <com.lee.TitleLayout android:layout_width="match_parent" android:layout_height="wrap_content" />
ListView 简单用法 适配ListView,将数据通过适配器传递给ListView。
1 2 3 ArrayAdapter <String> adapter = new ArraAdapter<String>(MainActivity.this, android.R.layout.simple_list_item_1, String[]); listView.setAdapter(adapter);
定制ListView 定制一个水果名+图片的ListView。 首先定义一个实体类,作为ListView适配器的适配类型。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class Fruit { private String name; private int imageId; public Fruit (String name, int imageId) { this .name = name; this .imageId = imageId; } public String getName () { return name; } public String getImageId () { return imageId; } }
然后需要为ListView的子项指定一个布局fruit_item.xml。
1 2 3 4 <LinearLayout ....> <ImageView ..../> <TextView ..../> </LinearLayout>
接下来需要继承ArrayAdapter创建一个自定义适配器FruitAdapter。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class FruitAdapter extends ArrayAdapter <Fruit> { private int resourceId; public FruitAdapter (Context context, int Id, List<Fruit> objects) { super (...); resourceId = Id; } @override public View getView (int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); View view = LayoutInflater.from(getContext).inflate(resourceId, parent, false ); ImageView fruitImage = view.findViewById(...); TextView fruitName = View.findViewById(...); fruitImage.setImgageResource(fruit.getImageId(); fruitName.setText(Fruit.getName(); return view; } }
关于inflate()方法,第三个参数false表示只让在父布局中声明的layout属性生效,但不为这个View添加父布局,因View有了父布局后,就不能添加到ListView中了。 适配ListView,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public class MainActivity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList <>(); onCreate() { ... initFruits(); FruitAdapter adapter = new FruitAdapter (MainActivity.this ,R.layout.fruit_item.xml,fruitList); listView.setAdapter(adapter); } initFruits() { Fruit apple = new Fruit ("Apple" , R.drawble.apple_pic); fruitList.add(apple); ... } }
提升ListView的效率 目前getView()时,每次加载子项都会重新加载布局,这样给了一个提升效率的方式。
1 2 3 4 5 6 7 8 9 10 public View getView (int position, View convertView, ViewGroup parent) { ... if (convertView == null ) { view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false ); } else { view = convertView; } ... }
不过目前getView每次都要同findViewById()获取硬件实例,所以可以通过ViewHolder优化。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public View getView (int position, View convertView, ViewGroup parent) { ... if (convertView == null ) { view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false ); viewHolder = new ViewHolder (); viewHolder.fruitImage = view.findViewById(...); viewHolder.fruitName = view.findViewById(...); view.setTag(viewHolder); } else { view = convertView; viewHolder = (ViewHolder)view.getTag(); } ... } Class ViewHolder { ImageView fruitImage; TextView fruitName; }
ListView的点击事件 1 2 3 4 5 6 listView.setOnItemClickListener(new AdapterView .onItemClickListener) { onItemClick(AdapterView<?> parent, View view , int position, long id) { Fruit fruit = fruitList.get(position); ... } }
RecyclerView 基本用法 首先需要再build.gradle中添加依赖库。
1 2 3 dependencies{ compile 'com.android.support:recyclerview-v7:24.2.1' }
然后在activity_main.xml中添加布局:
1 2 3 4 5 6 <LinearLayout <android.support.v7.widget.RecyclerView android:id="" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>
接下来需准备一个适配器类FruitAdapter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 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) { suiper(view); fruitImage = (ImageView) view.findViewById(); fruitName = (TextView) view.findViewById(); } } 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(); } }
适配器准备好后,就可以使用RecyclerView,修改MainActivity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class MainActivity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<>(); onCreate () { initFruits(); RecyclerView recyclerView = (RecyclerView)findViewById(); LinearLayoutManager layoutManager = new LinearLayoutManager(this); // 用于指定RecyclerView的布局方式,此指定为线性布局 recyclerView.setLayoutManager(layoutManager); FruitAdapter adapter = new FruitAdapter(fruitList); recyclerView.setAdapter(adapter); } private void initFruits(){ Fruit apple = new Fruit("Apple", R.drawable.apple_pic); fruitLIst.add(apple); ... } }
横向滚动和瀑布流布局 实现横向滚动的话,为了好看,需要将fruit_item.xml中图片和文字控件改为竖向布局。
1 2 <LinearLaout android:orientation = "vertical" >
然后再MainActivity中设置为横向滚动:
1 2 3 onCreate { layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); }
RecyclerView除了提供LinearLayoutManager布局排列外,还提供了如下两种布局排列:
1 2 GridLayoutManager:用于实现网格布局 StaggeredGridLayoutManager:用于实现瀑布流布局(类似于表格,不过根据内容多少适配显示)
实现瀑布流,可以对fruit_item.xml做一些针对性的调整,然后修改MainActivity,如下:
1 2 3 onCreate() { StaggeredGridLayoutManger layoutManager = new StaggeredGridLayoutManager (3 , StaggeredGridLayoutManger.VERTICAL); }
RecyclerView点击事件 RecyclerView需要针对具体view去注册,这样也能响应子项的点击事件。修改FruitAdapter,如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 FruitAdapter { ViewHolder { View fruitView; ViewHolder(View view){ fruitView = view; } } onCreateViewHolder() { ... final ViewHolder holder = new ViewHolder (view); holder.fruitView.setOnclickListener(new View .OnCickListener () { onClick(View v) { int position = holder.getAdapterPosition(); Fruit fruit = mFruitList.get(position); } } ); holder.fruitImage.setOnclickListener(...); } }
界面最佳实践 这个实践是一个做一个简单的聊天界面。
Nine-Patch图片 为了防止图片被拉伸变形, 需要对信息背景的图片进行处理,windows下sdk/tools下有一个draw9patch.bat工具脚本,可以用来制作Nine-Patch图片,Ubuntu下没找到。
编写界面 因为会用RecyclerView,所以第一件事就是添加依赖:
1 compile 'com.android.support:recyclerview-v7:24.2.1'
编写主界面activity_main.xml:
1 2 3 4 5 6 7 <LinearLayout> <android.support.v7.widget.RecyclerView/> // 信息界面 <LinearLayout> <EditText> // 编辑框 <Button> // 发送按钮 </LinearLayout> </LinearLayout>
定义一个消息实体类:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public class Msg { public static final int TYPE_RECEIVED = 0 ; public static final int TYPE_SENT = 1 ; private String content; private int type; public Msg (String content, int type) { this .content = content; this .type = type; } public String getContent () { return content; } public int getType () { return type; } }
消息类写好了,还需要写RecyclerView的子项布局文件msg_item.xml,如下:
1 2 3 4 5 6 7 8 9 <LinearLayout> <LinearLayout > //background设为制作的Nine-Patch消息背景图片 <TextView> // 收到消息 </LinearLayout> <LinearLayout > //background设为制作的Nine-Patch消息背景图片 <TextView> // 发出的消息 </LinearLayout> </LinearLayout>
此处子项布局文件是将发送和接收写在一起的,可以动可见属性来决定显示和隐藏。
接下来就该写适配器类MsgAdapter:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 public class MsgAdapter extends RecyclerView .Adapter<MsgAdapter.ViewHolder> { private List<Msg> mMsgList; public MsgAdapter (List<Msg> msgList) { mMsgList = msgList; } static class ViewHolder extends RecyclerView .ViewHolder { LinearLayout leftLayout; LinearLayout rightLayout; TextView leftMsg; TextView rightMsg; public ViewHolder (View itemView) { super (itemView); leftLayout = (LinearLayout) itemView.findViewById(R.id.left_layout); rightLayout = (LinearLayout) itemView.findViewById(R.id.right_layout); leftMsg = (TextView) itemView.findViewById(R.id.left_msg); rightMsg = (TextView) itemView.findViewById(R.id.right_msg); } } @Override public ViewHolder onCreateViewHolder (ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.msg_item, parent, false ); return new ViewHolder (view); } @Override public void onBindViewHolder (ViewHolder holder, int position) { Msg msg = mMsgList.get(position); if (msg.getType() == Msg.TYPE_RECEIVED){ holder.leftLayout.setVisibility(View.VISIBLE); holder.rightLayout.setVisibility(View.GONE); holder.leftMsg.setText(msg.getContent()); } else if (msg.getType() == Msg.TYPE_SENT) { holder.rightLayout.setVisibility(View.VISIBLE); holder.leftLayout.setVisibility(View.GONE); holder.rightMsg.setText(msg.getContent()); } } @Override public int getItemCount () { return mMsgList.size(); } }
最后在MainActivity中做一些处理即可实现一个聊天界面了。如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 onCreate(Bundle savedInstanceState) { ... initMsgs(); msgRecyclerView = findViewById(); LinearLayoutManager layoutManager = new LinearLayoutManager (this ); msgRecyclerView.setLayoutManager(layoutManager); adapter = new MsgAdapter (mMsgLists); msgRecyclerView.setAdapter(adapter); send = (Button) findViewById(R.id.send); inputText = (EditText) findViewById(R.id.input_text); send.setOnClickListener(new View .OnClickListener() { @Override public void onClick (View v) { String content = inputText.getText().toString(); if (!"" .equals(content)) { Msg msg = new Msg (content,Msg.TYPE_SENT); mMsgLists.add(msg); adapter.notifyItemInserted(mMsgLists.size()-1 ); msgRecyclerView.scrollToPosition(mMsgLists.size()-1 ); inputText.setText("" ); } else { Toast.makeText(MainActivity.this , "The input text can not be emputy!" , Toast.LENGTH_SHORT).show(); } } }); private void initMsgs () { Msg msg1 = new Msg ("Hello , this is msg1!" , Msg.TYPE_RECEIVED); mMsgLists.add(msg1); ... }