《第一行代码》控件、布局、碎片
8、控件
TextView:
android:gravity 来指定文字的对齐方式,可选值有 top、bottom、left、right、center等 , 可以用 “ | ” 来同时指定多个值,这里我们指定的 "center",效果等同于
"center_vertical|center_horizontal",表示文字在垂直和水平方向都居中对齐
android:textSize 属性可以指定文字的大小
android:textColor 属性可以指定文字的颜色
button:
方式1:
button = (Button) findViewById(R.id.button); button.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { // 在此处添加逻辑 } });
方式2:
不喜欢使用匿名类的方式来注册监听器,也可以使用实现接口的方式来进行注册
活动实现OnClickListener接口,然后重载onClick方法。
public class MainActivity extends Activity implements OnClickListener{ @Override public void onClick(View v) { switch (v.getId()) { case R.id.button: // 在此处添加逻辑 break; default: break; } } }
EditText:
android:hint="Type something here" 提示文字
android:maxLines 指定了 EditText 的最大行数为两行
ImageView:
android:src="@drawable/ic_launcher"
drawable 文件夹下已经有一张ic_launcher.png图片
imageView.setImageResource(R.drawable.jelly_bean);
AlertDialog:
setCancelable(false);
setCancelable()方法将对话框设为不可取消,否则用户按一下 Back 键就可以关闭对话框继续使用程序了
setPositiveButton()
给对话框注册确定按钮
AlertDialog.Builder dialog = new AlertDialog.Builder(SecondActivity.this); dialog.setTitle("This is Dialog"); dialog.setMessage("Something important."); dialog.setCancelable(false); dialog.setPositiveButton("OK", new DialogInterface. OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); dialog.setNegativeButton("Cancel", new DialogInterface. OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { } }); dialog.show();
ListView:
android.R.layout.simple_list_item_1 作为ListView 子项布局的 id,这是一个 Android 内置
的布局文件,里面只有一个 TextView,可用于简单地显示一段文本 ArrayAdapter 这种实现
ListView 是最容易实现,但最不容易扩展的实现方法 ArrayAdapter 的构造函数中依次传入
当前上下文、ListView 子项布局的 id,以及要适配的数据
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);
定制 ListView 的界面
1、编写实体类Fruit
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 int getImageId() { return imageId; } }
2、新建fruit_item.xml 定义每一项的展示方式布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <ImageView android:id="@+id/fruit_image" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <TextView android:id="@+id/fruit_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_marginLeft="10dip" /> </LinearLayout>
3、创建一个自定义的适配器,这个适配器继承自 ArrayAdapter,并将泛型指定为 Fruit 类
public class FruitAdapter extends ArrayAdapter<Fruit> { private int resourceId; //textViewResourceId:ListView 子项布局的 id public FruitAdapter(Context context, int textViewResourceId,List<Fruit> objects) { super(context, textViewResourceId, objects); resourceId = textViewResourceId; } @Override public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); // 获取当前项的Fruit实例 View view = LayoutInflater.from(getContext()).inflate(resourceId, null); 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; } }
FruitAdapter 重写了父类的一组构造函数,用于将上下文、ListView 子项布局的 id 和数据都传递进来。
另外又重写了 getView()方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。
在 getView 方法中,首先通过 getItem()方法得到当前项的 Fruit 实例,然后使用LayoutInflater 来为这个子项加载我们传入的布局,接着调用 View 的 findViewById()方法分别获取到 ImageView 和 TextView 的实例,并分别调用它们的 setImageResource()和 setText()方法来设置显示的图片和文字,最后将布局返回
4、主活动中添加list_view
initFruits(); // 初始化水果数据 FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item, fruitList); ListView listView = (ListView) findViewById(R.id.list_view); listView.setAdapter(adapter); private List<Fruit> fruitList = new ArrayList<Fruit>(); private void initFruits() { Fruit apple = new Fruit("Apple", R.drawable.apple_pic); fruitList.add(apple) Fruit mango = new Fruit("Mango", R.drawable.mango_pic); fruitList.add(mango); }
提升 ListView 的运行效率
目前我们 ListView的运行效率是很低的,因为在 FruitAdapter的 getView()方法中每次都将布局重新加载了一遍,当 ListView 快速滚动的时候这就会成为性能的瓶颈
getView()方法中还有一个 convertView 参数,这个参数用于将之前加载好的布局进行缓存,以便之后可以进行重用
public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); View view; if (convertView == null) { view = LayoutInflater.from(getContext()).inflate(resourceId, null); } else { view = convertView; } 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; } }虽然现在已经不会再重复去加载布局,但是每次在 getView()方法中还是会调用 View的 findViewById()方法来获取一次控件的实例。
我们可以借助convertView.setTag(obj)来对这部分性能进行优化
public View getView(int position, View convertView, ViewGroup parent) { Fruit fruit = getItem(position); View view; ViewHolder viewHolder; if (convertView == null) { view = LayoutInflater.from(getContext()).inflate(resourceId, null); 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.setOnItemClickListener(new OnItemClickListener() { 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(); } });...
9、布局
LinearLayout :
又称作线性布局,是一种非常常用的布局。
这个布局会将它所包含的控件在线性方向上依次排列
android:orientation="horizontal"
当LinearLayout 的排列方向是 horizontal 时,只有垂直方向上的对齐方式才会生效,因为此时水平方向上的长度是不固定的,每添加一个控件,水平方向上的长度都会改变,因而无法指定该方向上的对齐方式。同样的道理,
当 LinearLayout 的排列方向是 vertical 时,只有水平方向上的对齐方式才会生效。
android:layout_height="150px"
RelativeLayout:
又称作相对布局,也是一种非常常用的布局。它可以通过相对定位的方式让控件出现在布局的任何位置。
相对于父布局定位
android:layout_alignParentLeft="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true
android:layout_centerInParent="true"
相对于控件定位
android:layout_above 属性可以让一个控件位于另一个控件的上方,需要为这个属性指定相对控件 id 的引用,这里我们填入了 @id/button3,表示让该控件位于Button 3的上方 。
android:layout_below 表示让一个控件位于另一个控件的下方
android:layout_toLeftOf 表示让一个控件位于另一个控件的左侧
android:layout_toRightOf 表示让一个控件位于另一个控件的右侧。
注意,当一个控件去引用另一个控件的 id 时,该控件一定要定义在引用控件的后面,
不然会出现找不到 id 的情况
android:layout_alignLeft="@id/btn1" 表示让一个控件跟另一个控件左侧对齐
FrameLayout:
所有的控件都会摆放在布局的左上角
它的应用场景并不多
TableLayout:
使用表格的方式来排列控件
这种布局也不是很常用
android:layout_span="2" 合并单元格,让登录按钮占据两列的空间
android:stretchColumns 的值指定为 1,表示如果表格不能完全占满屏幕宽度,将第二列进行拉伸。没错!指定成 1 就是拉伸第二列,指定成 0 就是拉伸第一列
TableRow 中无法指定控件的宽度。这时使用android:stretchColumns 属性就可以很好地解决这个问题,它允许将 TableLayout 中的某一列进行拉伸,以达到自动适应屏幕宽度的作用
控件中:
android:gravity 是用于指定文字在控件中的对齐方式,
android:layout_gravity 是用于指定控件在布局中的对齐方式 。
android:background 用于为布局或控件指定一个背景
android:layout_width="0dp"
android:layout_weight="1"
由于我们使用了 android:layout_weight 属性,此时控件的宽度就不应该再由 android:layout_width 来决定,这里指定成 0 是一种比较规范的写法。
然后我们在 EditText 和 Button 里都将 android:layout_weight 属性的值指定为 1,这表示EditText 和 Button 将在水平方向平分宽度
为什么将 android:layout_weight 属性的值同时指定为 1 就会平分屏幕宽度呢?其实原理也很简单,系统会先把 LinearLayout 下所有控件指定的 layout_weight 值相加,得到一个总值,然后每个控件所占大小的比例就是用该控件的 layout_weight 值除以刚才算出的总值。
因此如果想让 EditText 占据屏幕宽度的 3/5,Button 占据屏幕宽度的 2/5,只需要将 EditText 的layout_weight 改成 3,Button 的 layout_weight 改成 2 就可以了。
我们仅指定了 EditText 的 android:layout_weight=1 属性,并将 Button 的宽度改回wrap_content。这表示 Button 的宽度仍然按照 wrap_content 来计算,而 EditText 则会占满屏幕所有的剩余空间。
android:layout_margin 可以指定控件在上下左右方向上偏移的距离
android:layout_marginLeft 或 android:layout_marginTop 等属性来单独指定控件在某个方向上偏移的距离
自定义控件
我们所用的所有控件都是直接或间接继承自 View 的,所用的所有布局都是直接或间接继承自 ViewGroup 的。
View 是 Android 中一种最基本的 UI 组件,它可以在屏幕上绘制一块矩形区域,并能响应这块区域的各种事件,因此,我们使用的各种控件其实就是在 View 的基础之上又添加了各自特有的功能。
ViewGroup 则是一种特殊的 View,它可以包含很多的子 View 和子 ViewGroup,是一个用于放置控件和布局的容器。
新建标题头布局文件title.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/title_bg" > <Button android:id="@+id/title_back" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="5dip" android:background="@drawable/back_bg" android:text="Back" android:textColor="#fff" /> <TextView android:id="@+id/title_text" android:layout_width="0dip" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_weight="1" android:gravity="center" android:text="Title Text" android:textColor="#fff" android:textSize="24sp" /> <Button android:id="@+id/title_edit" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:layout_margin="5dip" android:background="@drawable/edit_bg" android:text="Edit" android:textColor="#fff" /> </LinearLayout>
在主布局文件activity_main.xml引入控件title.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" > <include layout="@layout/title" /> </LinearLayout>
创建自定义控件
如果布局中有一些控件要求能够响应事件,我们还是需要在每个活动中为这些控件单独编写一次事件注册的代码。
比如说标题栏中的返回按钮,其实不管是在哪一个活动中,这个按钮的功能都是相同的,即销毁掉当前活动。而如果在每一个活动中都需要重新注册一遍返回按钮的点击事件,无疑又是增加了很多重复代码,这种情况最好是使用自定义控件的方式来解决
LayoutInflater
动态加载布局文件
通过 LayoutInflater 的 from()方法可以构建出一个 LayoutInflater对象,然后调用 inflate()方法就可
以动态加载一个布局文件,inflate()方法接收两个参数,第一个参数是要加载的布局文件的 id,这里我们传入
R.layout.title,第二个参数是给加载好的布局再添加一个父布局,这里我们想要指定为 TitleLayout,于是直接
传入 this。
public class TitleLayout extends LinearLayout { public TitleLayout(Context context, AttributeSet attrs) { super(context, attrs); //动态加载title.xml布局文件 LayoutInflater.from(context).inflate(R.layout.title, this); Button titleBack = (Button) findViewById(R.id.title_back); Button titleEdit = (Button) findViewById(R.id.title_edit); titleBack.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { ((Activity) getContext()).finish(); } }); titleEdit.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Toast.makeText(getContext(), "You clicked Edit button", Toast.LENGTH_SHORT).show(); } }); } }
在布局文件中添加这个自定义控件,修改activity_main.xml 中的代码,添加自定义控件:
<com.example.uicustomviews.TitleLayout android:layout_width="match_parent" android:layout_height="wrap_content"> </com.example.uicustomviews.TitleLayout>
添加自定义控件和添加普通控件的方式基本是一样的,只不过在添加自定义控件的时候我们需要指明控件的完整类名,包名在这里是不可以省略的
在实际开发中LayoutInflater这个类还是非常有用的,它的作用类似于findViewById()。不同点是LayoutInflater是用来找res/layout/下的xml布局文件,并且实例化;而findViewById()是找xml布局文件下的具体widget控件(如Button、TextView等)。
具体作用:
1、对于一个没有被载入或者想要动态载入的界面,都需要使用LayoutInflater.inflate()来载入;
2、对于一个已经载入的界面,就可以使用Activiyt.findViewById()方法来获得其中的界面元素。
LayoutInflater 是一个抽象类,在文档中如下声明:
public abstract class LayoutInflater extends Object
获得 LayoutInflater 实例的三种方式
1. LayoutInflater inflater = getLayoutInflater();//调用Activity的getLayoutInflater()
2. LayoutInflater inflater = LayoutInflater.from(context);
3. LayoutInflater inflater = (LayoutInflater)context.getSystemService
(Context.LAYOUT_INFLATER_SERVICE);
结论:所以这三种方式最终本质是都是调用的Context.getSystemService()。
inflater(resId, null )的确不能正确处理宽高的值
inflater(resId,parent,false)可以看出完美的显示了宽和高
而inflater(resId,parent,true)报错了
Android LayoutInflater深度解析 给你带来全新的认识
http://blog.csdn.net/lmj623565791/article/details/38171465
9、碎片
...