它主要用于在界面上显示一段文本信息
<TextView android:id="@+id/text_view" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:textSize="24sp" android:textColor="#00ff00" android:text="This is TextView"/>android:id 给当前控件定义一个唯一标识符
android:layout_width 和 android:layout_height指定控件的宽度和高度。Android所有的空间都有这两个属性,可选值三种, match_parent表示让当前控件的大小和父布局大小一样。也就是由父布局决定控件的大小 fill_parent 和 match_parent相似 wrap_content 表示让当前空间的大小能够刚好包含住里面的内容。也就是与控件的内容决定控件的大小
android:text指定TextView中显示的文本
android:gravity 来指定文字的对齐方式,可选值有top,bottom,left,right,center等,可以用 | 来同时指定多个值,这里我们指定的center,效果等同于center_vertical|center_horizontal,表示文字在垂直和水平方向都居中对齐。
android:textSize 属性可以指定文字的大小
android:textColor属性可以指定文字的颜色
Toast 是Android 系统提供的一种非常好的提醒方式,在程序中可以使用它将一些短小的信息通知给用户,这些信息在一段时间后会自动消失,并且不会占用任何屏幕空间。
该方法的一般用法: Toast toast = Toast.makeText(context, “”, time); 这三个参数分别是: 1.当前的上下文环境;(getApplicationContext这个方法可以获取) 2.要显示的字符串;(就是一般的字符串,可以写在string.xml中) 3.显示的时间长短;(有toast默认的参数,也可以自己设定)
Toast.makeText(FirstActivity.this,"i learn english", Toast.LENGTH_SHORT).show();多选框,一个CheckBox对应一个框。 注册点击事件监听器
public class MainActivity extends AppCompatActivity { private CheckBox eatBox; private CheckBox sleepBox; private CheckBox playBox; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); eatBox = (CheckBox) findViewById(R.id.eatBox); sleepBox = (CheckBox) findViewById(R.id.sleepBox); playBox = (CheckBox) findViewById(R.id.playBox); OnBoxClickListener listener = new OnBoxClickListener(); eatBox.setOnClickListener(listener); sleepBox.setOnClickListener(listener); playBox.setOnClickListener(listener); } class OnBoxClickListener implements View.OnClickListener{ @Override public void onClick(View view) { CheckBox box = (CheckBox) view; String Msg = ""; if(box.getId() == R.id.eatBox) Msg = "eatBox"; else if(box.getId() == R.id.sleepBox) Msg = "sleepBox"; else if(box.getId() == R.id.playBox) Msg = "playBox"; if(box.isChecked()) Msg += " checked!"; else Msg += " unchecked!"; Toast.makeText(MainActivity.this,Msg,Toast.LENGTH_SHORT).show(); } } }注册状态改变事件监听器
public class MainActivity extends AppCompatActivity { private CheckBox eatBox; private CheckBox sleepBox; private CheckBox playBox; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); eatBox = (CheckBox) findViewById(R.id.eatBox); sleepBox = (CheckBox) findViewById(R.id.sleepBox); playBox = (CheckBox) findViewById(R.id.playBox); CheckBoxListener listener = new CheckBoxListener(); eatBox.setOnCheckedChangeListener(listener); sleepBox.setOnCheckedChangeListener(listener); playBox.setOnCheckedChangeListener(listener); } class CheckBoxListener implements CompoundButton.OnCheckedChangeListener { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean b) { String Msg = ""; if(compoundButton.getId() == R.id.playBox) if(b) Msg = "playBox checked!"; else Msg = "PlayBox unchecked!"; else if(compoundButton.getId() == R.id.sleepBox) if(b) Msg = "sleepBox checked!"; else Msg = "sleepBox unchecked!"; else if(compoundButton.getId() == R.id.eatBox) if(b) Msg = "eatBox checked!"; else Msg = "eatBox unchecked!"; Toast.makeText(MainActivity.this,Msg,Toast.LENGTH_SHORT).show(); } } }isChecked() 判断当前框是否被选中 setCheckted(boolean)设置当前框的状态
单选按钮在逻辑上是成组出现的,即一组中只能选择一个按钮。 RadioButton 是 RadioGroup的字标签,xml文档中如下表示
<RadioGroup android:id="@+id/group1" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal"> <RadioButton android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="男性"/> <RadioButton android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="女性"/> </RadioGroup>注册OnCheckedChangeListener监听器的方式有两种,一个是为每一个按钮注册监听器,一个是为每一组注册一个监听器。
注意:两个监听器虽然名字都是·OnCheckedChangeListener·,但在不同包下,为按钮注册的监听器和多选按钮注册的包名一样 ,是CompoundButton;而为组对象注册的监听器包是RadioGroup。
下面是选择为组对象注册监听器
public class MainActivity extends AppCompatActivity { private RadioGroup radioGroup; private RadioButton radioButton1; private RadioButton radioButton2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); radioGroup = (RadioGroup) findViewById(R.id.group1); radioButton1 = (RadioButton) findViewById(R.id.button1); radioButton2 = (RadioButton) findViewById(R.id.button2); RadioGroupListener listener = new RadioGroupListener(); radioGroup.setOnCheckedChangeListener(listener); } class RadioGroupListener implements RadioGroup.OnCheckedChangeListener{ @Override public void onCheckedChanged(RadioGroup radioGroup, int i) { String Msg = ""; if(i == radioButton1.getId()) Msg = "male checked!"; else if(i == radioButton2.getId()) Msg = "female checked!"; Toast.makeText(MainActivity.this, Msg, Toast.LENGTH_SHORT).show(); } } }注意系统会默认将Button的所有英文字符自动进行大写转换,我们要通过android:textAllCaps属性来禁用它。
<Button android:id="@+id/button" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAllCaps="false" android:text="Button"/>然后我们在活动类中为Button的点击事件注册一个监听器 匿名类的方式来注册监听器
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view) { //再此处添加逻辑 } }); } }如果不喜欢这种匿名类的方式来注册监听器,也可以使用实现接口的方式来进行注册。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); button.setOnClickListener(this); } @Override public void onClick(View view) { switch(view.getId()){ case R.id.button: //在此处添加逻辑 break; default: break; } } }内部类实现注册监听器
public class MainActivity extends AppCompatActivity { private CheckBox eatBox; private CheckBox sleepBox; private CheckBox playBox; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); eatBox = (CheckBox) findViewById(R.id.eatBox); sleepBox = (CheckBox) findViewById(R.id.sleepBox); playBox = (CheckBox) findViewById(R.id.playBox); OnBoxClickListener listener = new OnBoxClickListener(); eatBox.setOnClickListener(listener); sleepBox.setOnClickListener(listener); playBox.setOnClickListener(listener); } class OnBoxClickListener implements View.OnClickListener{ @Override public void onClick(View view) { Toast.makeText(MainActivity.this,"hello",Toast.LENGTH_SHORT).show(); } }android:hint 属性让我们可以添加提示文字
android:maxLines 指定EditText的最大行数。当输入的文字超过指定的行数时,文本就会向上滚动,而EditText则不会在继续延伸
实例:Button 与 EditText结合
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private EditText editText; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); editText = (EditText) findViewById(R.id.edit_text); button.setOnClickListener(this); } @Override public void onClick(View view) { switch(view.getId()){ case R.id.button: String inputText = editText.getText().toString(); Toast.makeText(MainActivity.this,inputText, Toast.LENGTH_SHORT).show(); break; default: break; } } }ImageView是用于在界面上展示图片的一个控件,它可以让我们的程序世界变得更加丰富多彩。 图片通常都是放在以’drawable’开头的目录下的,并且指定分辨率。
android:src 属性给Image_View指定一张图片。 android:scaleType 属性 不匹配情况下拉伸图片
<ImageView android:id="@+id/image_view" android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@drawable/img_1"/>按钮事件来显示指定的图片
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private EditText editText; private ImageView imageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); editText = (EditText) findViewById(R.id.edit_text); imageView = (ImageView) findViewById(R.id.image_view); button.setOnClickListener(this); } @Override public void onClick(View view) { switch(view.getId()){ case R.id.button: imageView.setImageResource(R.drawable.img_2); break; default: break; } } }ProgressBar 用于在界面上显示一个进度条,表示我们的程序正在加载一些数据。
<ProgressBar android:id="@+id/progesss_bar" android:layout_width="match_parent" android:layout_height="wrap_content" style="?android:attr/progressBarStyleHorizontal" android:max="100"/>style属性设置ProgressBar样式
android:max 属性给进度条设置一个最大值 android:progress 属性 设置进度条当前进度值
怎么让进度条消失,所以android 控件都有这个属性,android:visibility,可选值有三种 visible 表示控件是课件的,默认值 invisible 表示控件不可见,但是仍然占据空间 gone表示控件不可见,也不占据空间 我们还可以通过代码设置控件的可见性,使用的是setVisibility()方法,可以传入View.VISIBLE,View.INVISIBLE和View.GONE这3种值。
通过点击按钮来增加进度值。
package com.example.uiwidgettest; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.EditText; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.Toast; public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private EditText editText; private ImageView imageView; private ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); editText = (EditText) findViewById(R.id.edit_text); progressBar = (ProgressBar) findViewById(R.id.progesss_bar); button.setOnClickListener(this); } @Override public void onClick(View view) { switch(view.getId()){ case R.id.button: int progress = progressBar.getProgress(); progress = progress + 10; progressBar.setProgress(progress); break; default: break; } } }可拖动的进度条。
public class MainActivity extends AppCompatActivity { private SeekBar seekBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); seekBar = (SeekBar) findViewById(R.id.s1); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { /** * * @param seekBar 触发了监听器的SeekBar对象 * @param progress 当前SeekBar对象的进度 * @param fromuser 是不是用户拖动导致进度条发生改变 */ @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromuser) { Toast.makeText(MainActivity.this,progress +" " +fromuser, Toast.LENGTH_SHORT).show(); } @Override public void onStartTrackingTouch(SeekBar seekBar) { Toast.makeText(MainActivity.this,"Star", Toast.LENGTH_SHORT).show(); } @Override public void onStopTrackingTouch(SeekBar seekBar) { Toast.makeText(MainActivity.this,"End", Toast.LENGTH_SHORT).show(); } }); } }AlertDialog 可以在当前的界面弹出一个对话框,这个对话框是置顶于所有界面元素之上的,能够屏蔽其他控件的交互能力,因此AlertDialog 一般都是用于提示一些非常重要的内容或者警告信息。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private EditText editText; private ImageView imageView; private ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); editText = (EditText) findViewById(R.id.edit_text); progressBar = (ProgressBar) findViewById(R.id.progesss_bar); button.setOnClickListener(this); } @Override public void onClick(View view) { switch(view.getId()){ case R.id.button: AlertDialog.Builder dialog= new AlertDialog.Builder(MainActivity.this); dialog.setTitle("This is Dialog"); dialog.setMessage("Something important."); dialog.setCancelable(false); dialog.setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //设置确定按钮的点击 } }); dialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { //设置取消按钮的点击事件 } }); AlertDialog dialog_entry = dialog.create(); dialog_entry.show();//显示对话框 dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(Color.parseColor("#fd8d99"));//给按钮换颜色 break; default: break; } } }首先通过AlertDialog.Builder创建一个AlertDialog的实例,然后可以为这个对话框设置标题、内容,可否取消等属性,接下来调用setPositiveButton()方法为对话框确定按钮的点击事件,调用setNegativeButton()方法设置取消按钮的点击事件,最后调用show()方法将对话框显示出来。
和 AlertDialog比较类似,不同的是,ProgressDialog会在对话框中显示一个进度条,一般用于表示当前操作比较耗时,让用户耐心等待。
public class MainActivity extends AppCompatActivity implements View.OnClickListener{ private EditText editText; private ImageView imageView; private ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button button = (Button) findViewById(R.id.button); editText = (EditText) findViewById(R.id.edit_text); progressBar = (ProgressBar) findViewById(R.id.progesss_bar); button.setOnClickListener(this); } @Override public void onClick(View view) { switch(view.getId()){ case R.id.button: ProgressDialog progressdialog= new ProgressDialog (MainActivity.this); progressdialog.setTitle("This is ProgressDialog"); progressdialog.setMessage("Loading..."); progressdialog.setCancelable(true); progressdialog.show();//显示对话框 break; default: break; } } }注意,如果在setCancelable()中传入了false,表示ProgressDialog是不能通过Back键取消掉的,这是你就一定要在代码中做好控制,当数据加载完成后必须要调用ProgressDialog的dismiss()方法来关闭对话框,否则ProgressDialog将会一直存在
时间控件 注册OnTimeChangedListener监听器(匿名类方式)
public class MainActivity extends AppCompatActivity { private TimePicker timePicker; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); timePicker = (TimePicker) findViewById(R.id.p1); timePicker.setIs24HourView(true); timePicker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() { @Override public void onTimeChanged(TimePicker timePicker, int i, int i1) { Toast.makeText(MainActivity.this,"Hour: "+i + " Minute: " + i1, Toast.LENGTH_SHORT).show(); } }); } }日期控件
当我们的程序有大量的数据需要展示的时候,就可以借助ListView来实现,它允许用户通过手指上下滑动的方式将屏幕外的数据滚动到屏幕内,同时屏幕上原有的数据则会滚出屏幕。
常用属性 分割线属性divider Item按下颜色listSelector 右侧快速滑动标签fastScroll 去除ListView滑到顶部和底部时边缘的黑色阴影 按下阴影显示Item之上drawSelectorOnTop 具体请访问链接
一个简单的用法 activity_main.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"> <ListView android:id="@+id/list_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>MainActivity.java
public class MainActivity extends AppCompatActivity { private String[] data = {"Apple","Banana","Orage","Watermelon", "Pear","Grape","Pineapple","Strawberry","Cherry","Mango", "Apple","Banana","Orage","Watermelon","Pear","Grape","Pineapple", "Strawberry","Cherry","Mango"}; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); setTitle("水果篮"); 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); } }这里我们简单用一个data数组测试,里面包含很多水果名称
不过,数组中的数据是无法直接传递给ListView的,我们还需要借助适配器来完成。Android中提供很多适配器的实现类,其中我认为最好用的就是ArrayAdapter,他可以借助泛型来指定要是适配的数据类型,然后在构造函数中把要适配的数据传入。 ArrayAdapter 有多个构造函数的重载,你应该根据实际情况选择最合适的一种。
定制ListView的界面 效果如下 我们定义一个实体类Fruit类,作为ListView适配器的适配类型。
public class Fruit { private String name;//水果名称 private int imageId;//水果图片 private String text;//水果描述 public Fruit(String name,int imageId,String text){ this.name = name; this.imageId = imageId; this.text = text; } public String getName(){ return name; } public int getImageId(){ return imageId; } public String getText(){ return text; } }然后为ListView的子项指定一个自定义的布局fruit_item.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"> <ImageView android:id="@+id/fruit_image" android:layout_width="wrap_content" android:layout_height="wrap_content"/> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical" > <TextView android:id="@+id/fruit_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center" /> <TextView android:id="@+id/fruitText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="bottom" android:layout_marginTop="10dp"/> </LinearLayout> </LinearLayout>然后创建一个自定义的适配器类FruitAdapter,这个适配器继承自ArrayAdapter,并 将泛型指定为Fruit.
public class FruitAdapter extends ArrayAdapter<Fruit> { private int resourceId; public FruitAdapter(@NonNull Context context, int resource, @NonNull List<Fruit> objects) { super(context, resource, objects); resourceId = resource; } @NonNull @Override public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) { Fruit fruit = getItem(position);//获取当前Fruit实例 View view; ViewHolder viewHolder; if(convertView == null){ view = LayoutInflater.from(getContext()).inflate(resourceId,parent,false); viewHolder = new ViewHolder(); viewHolder.fruitImage = (ImageView) view.findViewById(R.id.fruit_image); viewHolder.fruitName = (TextView) view.findViewById(R.id.fruit_name); viewHolder.fruitText = (TextView) view.findViewById(R.id.fruitText); view.setTag(viewHolder);//将ViewHolder存储在View中 } else{ view = convertView; viewHolder = (ViewHolder) view.getTag();//重新获取ViewHolder } viewHolder.fruitImage.setImageResource(fruit.getImageId()); viewHolder.fruitName.setText(fruit.getName()); viewHolder.fruitText.setText(fruit.getText()); return view; } class ViewHolder{ ImageView fruitImage; TextView fruitName; TextView fruitText; } }FruitAdapter重写了父类的一组构造方法,用于将上下文,ListView子项布局的id和数据都传进来。然后重写了getView方法,这个方法在每个子项被滚动到屏幕内的时候会被调用。 getView()方法中有个convertView参数,这个参数用于将之前加载好的布局进行缓存,以便重用,这样就不会每次都重新加载布局了。然后我们新建类ViewHolder用来解决每次都调用findViewById()来获取控件实例的问题。
接下来我们修改MainActivity.java代码
public class MainActivity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFruits();//初始化水果数据 FruitAdapter adapter = new FruitAdapter(MainActivity.this, R.layout.fruit_item,fruitList); ListView listView = (ListView) findViewById(R.id.lv1); listView.setAdapter(adapter); listView.setOnItemClickListener(new ListView.OnItemClickListener(){ @Override public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) { Fruit fruit = fruitList.get(i); Toast.makeText(MainActivity.this,fruit.getName(), Toast.LENGTH_SHORT).show(); } }); } private void initFruits() { for(int i = 0;i < 2;++ i){ Fruit apple = new Fruit("Apple",R.drawable.apple_pic,"This is an apple."); fruitList.add(apple); Fruit banana = new Fruit("Banana",R.drawable.banana_pic,"This is a banana."); fruitList.add(banana); Fruit orange = new Fruit("Orange",R.drawable.orange_pic,"This is an orange."); fruitList.add(orange); Fruit pear = new Fruit("Pear",R.drawable.pear_pic,"This is a pear."); fruitList.add(pear); } } }我们在onCreate()方法中创建FruitAdapter对象,并将FruitAdapter作为适配器传给ListView。这样页面就完成了。 最后我们使用setOnItemClickListener()方法为ListView注册一个监听器,当用户点击ListView中的任何一个子项时,就会回调onItemClick()方法。
更强大的滚动控件,可以说是增强版的ListView,不仅可以轻松实现和ListView同样的效果,还优化了ListView中存在的各种不足。
因为RecyclerView属于新增的控件,Android将RecyclerView定义在support库里。若要使用RecyclerView,第一步是要在build.gradle中添加对应的依赖库。
添加RecyclerView 依赖库 app/build.gradle中的dependencies闭包添加以下内容:
implementation 'com.android.support:recyclerview-v7:27.1.1'然后点击顶部的Sync Now进行同步
在activity_main.xml添加如下内容使用
<androidx.recyclerview.widget.RecyclerView android:id="@+id/recycler_view" android:layout_width="match_parent" android:layout_height="match_parent">这里我们简单实现如下界面 这和上面ListView实现的界面差不多。 照样写一个Fruit实体类,然后写一个子项布局fruit_item.xml
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 void setName(String name) { this.name = name; } public int getImageId() { return imageId; } public void setImageId(int imageId) { this.imageId = imageId; } } <?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="wrap_content"> <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_vertical" android:layout_marginLeft="10dp"/> </LinearLayout>然后为RecyclerView准备一个适配器FruitAdapter类,让这个类继承RecyclerView.Adapter,并将泛型指定为FruitAdapter.ViewHolder,其中ViewHolder是我们在FruitAdapter中定义的一个内部类。
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{ private List<Fruit> mFruitList; static class ViewHolder extends RecyclerView.ViewHolder{ ImageView fruitImage; TextView fruitName; public ViewHolder(@NonNull View itemView) { super(itemView); fruitImage = (ImageView) itemView.findViewById(R.id.fruit_image); fruitName = (TextView) itemView.findViewById(R.id.fruit_name); } } public FruitAdapter(List<Fruit> fruitList){ mFruitList = fruitList; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {//创建ViewHolder实例,并将fruit_item布局加载进来 View view = LayoutInflater.from(parent.getContext()).inflate( R.layout.fruit_item,parent,false ); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(@NonNull 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(); } }最后我们修改MainActivity.java的代码。
public class MainActivity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFruits();//初始化水果数据 RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); LinearLayoutManager layoutManager = new LinearLayoutManager(this);//LayoutManger用于指定RecyclerView的布局方式,这里LinearLayoutManger是线性布局的意思 recyclerView.setLayoutManager(layoutManager); FruitAdapter adapter = new FruitAdapter(fruitList); recyclerView.setAdapter(adapter); } private void initFruits(){ for(int i = 0;i < 2; i ++){ Fruit apple = new Fruit("Apple",R.drawable.apple_pic); fruitList.add(apple); Fruit banana = new Fruit("banana",R.drawable.banana_pic); fruitList.add(banana); Fruit orange = new Fruit("banana",R.drawable.orange_pic); fruitList.add(orange); Fruit watermelon = new Fruit("watermelon",R.drawable.watermelon_pic); fruitList.add(watermelon); } } }实现横向滚动和瀑布流布局 实现横向滚动,还是对上面的代码进行操作,效果如下。 我们要对子项布局fruit_item.xml进行布局上的些许修改,因为目前这个布局的元素是水平排列的,适合纵向滚动,我们要把子项布局中元素改为纵向排列才比较合理。 fruit_item.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="100dp" android:layout_height="wrap_content"> <ImageView android:id="@+id/fruit_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal"/> <TextView android:id="@+id/fruit_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginTop="10dp"/> </LinearLayout>然后修改MainActivity.java的代码
public class MainActivity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFruits(); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); LinearLayoutManager layoutManager = new LinearLayoutManager(this); layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);//设置布局的排列方向,默认纵向布局 recyclerView.setLayoutManager(layoutManager); FruitAdapter adapter = new FruitAdapter(fruitList); recyclerView.setAdapter(adapter); } private void initFruits(){ for(int i = 0;i < 2; i ++){ Fruit apple = new Fruit("Apple",R.drawable.apple_pic); fruitList.add(apple); Fruit banana = new Fruit("banana",R.drawable.banana_pic); fruitList.add(banana); Fruit orange = new Fruit("banana",R.drawable.orange_pic); fruitList.add(orange); Fruit watermelon = new Fruit("watermelon",R.drawable.watermelon_pic); fruitList.add(watermelon); } } }除了LinearManger之外,RecyclerView还给我们提供了GirdLayoutManger和StaggeredGirLayoutManger两种内置的布局方式,GirdLayoutManger可以用来实现网格布局,StaggeredGirLayoutManger可以用于实现瀑布流布局。
下面我们先实现下瀑布流布局。 效果如下 还是先修改下子项布局fruit_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_margin="5dp"> <ImageView android:id="@+id/fruit_image" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal"/> <TextView android:id="@+id/fruit_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="left" android:layout_marginTop="10dp"/> </LinearLayout>然后修改MainActivity.java
public class MainActivity extends AppCompatActivity { private List<Fruit> fruitList = new ArrayList<>(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initFruits(); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view); StaggeredGridLayoutManager layoutManager = new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager);//第一个参数是布局列数,第二个参数是布局的排列方向 FruitAdapter adapter = new FruitAdapter(fruitList); recyclerView.setAdapter(adapter); } private void initFruits(){ for(int i = 0;i < 2; i ++){ Fruit apple = new Fruit(getRandomLengthName("Apple"),R.drawable.apple_pic); fruitList.add(apple); Fruit banana = new Fruit(getRandomLengthName("banana"),R.drawable.banana_pic); fruitList.add(banana); Fruit orange = new Fruit(getRandomLengthName("banana"),R.drawable.orange_pic); fruitList.add(orange); Fruit watermelon = new Fruit(getRandomLengthName("watermelon"),R.drawable.watermelon_pic); fruitList.add(watermelon); } } private String getRandomLengthName(String name){//随机生成1-20之间的数,将字符串重复几遍,更好的显示布局效果 Random random = new Random(); int length = random.nextInt(20) + 1; StringBuilder builder = new StringBuilder(); for(int i = 0;i < length;++ i){ builder.append(name); } return builder.toString(); } }RecyclerView的点击事件 它并没有提供现有的监听器供我们使用,而是需要我们自己给子项具体的View去注册事件。
我们修改FruitAdapter.java中的代码
public class FruitAdapter extends RecyclerView.Adapter<FruitAdapter.ViewHolder>{ private List<Fruit> mFruitList; static class ViewHolder extends RecyclerView.ViewHolder{ View fruitView; ImageView fruitImage; TextView fruitName; public ViewHolder(@NonNull View itemView) { super(itemView); fruitView = itemView; fruitImage = (ImageView) itemView.findViewById(R.id.fruit_image); fruitName = (TextView) itemView.findViewById(R.id.fruit_name); } } public FruitAdapter(List<Fruit> fruitList){ mFruitList = fruitList; } @NonNull @Override public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate( R.layout.fruit_item,parent,false ); final ViewHolder holder = new ViewHolder(view); holder.fruitView.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view) { int position = holder.getAdapterPosition(); Fruit fruit = mFruitList.get(position); Toast.makeText(view.getContext(),"you clicked view " + fruit.getName(), Toast.LENGTH_SHORT).show(); } }); holder.fruitImage.setOnClickListener(new View.OnClickListener(){ @Override public void onClick(View view) { int position = holder.getAdapterPosition(); Fruit fruit = mFruitList.get(position); Toast.makeText(view.getContext(),"you clicked image " + fruit.getName(), Toast.LENGTH_SHORT).show(); } }); return holder; } @Override public void onBindViewHolder(@NonNull 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(); } }我们先是在ViewHolder中添加fruitView来保存子项最外层的布局实例,然后注册点击事件,这里将最外层的布局和ImageView都注册了点击事件。在点击事件中,先是获取用户点击的positon,然后通过position获取Fruit实例,在用Toast弹出信息。
借助它我们可以在自己的应用程序里嵌套一个浏览器,从而非常轻松的来展示各种各样的网页。 例子,在应用内打开百度。新建一个WebViewTest项目 修改activity_main.xml代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <WebView android:id="@+id/web_view" android:layout_width="match_parent" android:layout_height="match_parent"/> </LinearLayout>然后修改MainActivity代码 借助getSettings()方法可以去设置一些浏览器的属性,这里我们并不去设置过多属性,只是调用setJavaScriptEnabled()方法来让WebView支持JavaScript脚本 然后调用WebView的setWebViewClient()方法,并传入一个WebViewClient实例,这段代码的作用是,当需要从一个网页跳转到另一个网页时,我们希望目标网页仍然在当前WebView中显示,而不是打开系统浏览器。最后就是调用WebView的loadUrl()方法并将网址传入,即可展示相应的内容。 另外本程序使用到了网络功能,而访问网络是需要声明权限的,因此我们还得修改AndroidManifest.xml文件,并加入权限声明,并且android10.0对未加密的流量不信任,需要再加入新的限制,我么需要在<aplication 标签内 设置usesCleartextTraffic 属性为true
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.webview"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:usesCleartextTraffic="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>这样就可以访问百度了。
简介 ScrollView称为滚动视图,当在一个屏幕的像素显示不下绘制的UI控件时,可以采用滑动的方式,使控件显示。
先看下ScrollView类的继承关系:
java.lang.Object ↳android.view.View ↳android.view.ViewGroup ↳android.widget.FrameLayout ↳android.widget.ScrollViewz可以看出,ScrollView原来是一个FrameLayout的容器,不过在他的基础上添加了滚动,允许显示的比实际多的内容。 使用方式 1.竖直滚动视图ScrollView 在页面的竖直方向线性布局5个Button,代码如下:
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <Button android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="20dp" android:gravity="center" android:text="内容一" android:textColor="#03A9F4" android:textSize="24sp" /> <Button android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="80dp" android:gravity="center" android:text="内容二" android:textColor="#03A9F4" android:textSize="24sp" /> <Button android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="80dp" android:gravity="center" android:text="内容三" android:textColor="#03A9F4" android:textSize="24sp" /> <Button android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="80dp" android:gravity="center" android:text="内容四" android:textColor="#03A9F4" android:textSize="24sp" /> <Button android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="80dp" android:layout_marginBottom="80dp" android:gravity="center" android:text="内容五" android:textColor="#03A9F4" android:textSize="24sp" /> </LinearLayout> </ScrollView>通过Android Studio的Preview视图也可以看出,5个Button已超出屏幕显示,在不使用ScrollView的情况下,父布局直接使用LinearLayout,是无法使屏幕滑动显示所有控件的。
使用ScrollView后显示如下: 注意:ScrollView的子元素只能有一个,可以是一个View(如ImageView、TextView等) 也可以是一个ViewGroup(如LinearLayout、RelativeLayout等),其子元素内部则不再限制,否则会报以下异常。
Caused by: java.lang.IllegalStateException: ScrollView can host only one direct child2.水平滚动视图HorizontalScrollView 在实际使用时,我们也会遇到水平方向,控件超出屏幕的情况。这时就需要使用水平方向的滚动视图HorizontalScrollView。
在上面代码头部新增一个HorizontalScrollView,水平方向线性布局4个ImageView,代码如下:
<?xml version="1.0" encoding="utf-8"?> <ScrollView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <HorizontalScrollView android:layout_width="match_parent" android:layout_height="200dp" android:layout_marginTop="20dp"> <LinearLayout android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center_vertical" android:orientation="horizontal"> <ImageView android:layout_width="120dp" android:layout_height="120dp" android:layout_margin="20dp" android:src="@mipmap/ic_launcher" /> <ImageView android:layout_width="120dp" android:layout_height="120dp" android:layout_margin="20dp" android:src="@mipmap/ic_launcher" /> <ImageView android:layout_width="120dp" android:layout_height="120dp" android:layout_margin="20dp" android:src="@mipmap/ic_launcher" /> <ImageView android:layout_width="120dp" android:layout_height="120dp" android:layout_margin="20dp" android:src="@mipmap/ic_launcher" /> </LinearLayout> </HorizontalScrollView> <Button android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="20dp" android:gravity="center" android:text="内容一" android:textColor="#03A9F4" android:textSize="24sp" /> <Button android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="80dp" android:gravity="center" android:text="内容二" android:textColor="#03A9F4" android:textSize="24sp" /> <Button android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="80dp" android:gravity="center" android:text="内容三" android:textColor="#03A9F4" android:textSize="24sp" /> <Button android:layout_width="match_parent" android:layout_height="100dp" android:layout_marginTop="80dp" android:layout_marginBottom="80dp" android:gravity="center" android:text="内容四" android:textColor="#03A9F4" android:textSize="24sp" /> </LinearLayout> </ScrollView>可以看出,HorizontalScrollView中的图片内容,可以横向滑动,并且整个布局由于外部嵌套了ScrollView,整体页可以竖直方向滑动。
注意:同ScrollView,HorizontalScrollView中的子元素也只能有一个,否则报错。
XML中常用属性介绍 1.android:fadingEdge=“none”
设置拉滚动条时,边框渐变的方向。none(边框颜色不变),horizontal(水平方向颜色变淡),vertical(垂直方向颜色变淡)。
2.android:overScrollMode=“never”
删除ScrollView拉到尽头(顶部、底部),然后继续拉出现的阴影效果,适用于2.3及以上的 否则不用设置。
3.android:scrollbars=“none”
设置滚动条显示,none(隐藏),horizontal(水平),vertical(垂直)。
4.android:descendantFocusability=""
该属性是当一个为view获取焦点时,定义ViewGroup和其子控件两者之间的关系。 属性的值有三种:
beforeDescendants //viewgroup会优先其子类控件而获取到焦点 afterDescendants //viewgroup只有当其子类控件不需要获取焦点时才获取焦点 blocksDescendants //viewgroup会覆盖子类控件而直接获得焦点5.android:fillViewport=“true"
这是 ScrollView 独有的属性,用来定义 ScrollView 对象是否需要拉伸自身内容来填充 viewport。通俗来说,就是允许ScrollView去填充整个屏幕。比如ScrollView嵌套的子控件高度达不到屏幕高度时,虽然ScrollView高度设置了match_parent,也无法充满整个屏幕,需设置android:fillViewport=“true"使ScrollView填充整个页面,给ScrollView设置背景颜色就能体现。 常用方法: 滑动开关控制
scrollView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { // true禁止滑动 false可滑动 return true; } });滑动位置控制
scrollView.post(new Runnable() { @Override public void run() { //滑动到顶部 scrollView.fullScroll(ScrollView.FOCUS_UP); //滑动到底部 scrollView.fullScroll(ScrollView.FOCUS_DOWN); } });滑动到某个位置
scrollView.post(new Runnable() { @Override public void run() { //偏移值 int offset = 100; scrollView.smoothScrollTo(0, offset); } });结语 可以看出,ScrollView是在日常开发中经常使用的View控件,其使用方式也比较简单。