掌握Android UI设计之布局管理器
属性 | 属性 | 属性 |
---|---|---|
android:id | android:layout_width | android:layout_height |
android:background | android:layout_margin | android:layout_padding |
android:orientation | android:gravity | android:layout_weight |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="20dp"
tools:context=".MainActivity">
<TextView
android:layout_width="match_parent"
android:layout_height="200dp"
android:text="15521139529"
android:gravity="center"
>TextView>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码"
android:layout_marginRight="10dp"
android:gravity="center">TextView>
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="请填写微信密码"
android:inputType="textPassword"
android:maxLines="1"
>EditText>
LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="用短信验证码登录"
android:textColor=" #7D9EC0">TextView>
<Button
android:layout_width="match_parent"
android:layout_height="60dp"
android:text="登录"
android:layout_marginTop="20dp">Button>
LinearLayout>
属性 | 作用 |
---|---|
android:layout_toLeftOf | 在谁的左边 |
android:layout_toRightOf | 在谁的右边 |
android:layout_above | 在谁的上边 |
android:layout_below | 在谁的下边 |
android:layout_alignBottom | 跟谁的底部对齐 |
android:layout_alignParentBottom | 跟父控件底部对齐 |
android:layout_centerInParent | 在父组件的中间 |
代码:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="20dp"
android:id="@+id/relative"
tools:context=".MainActivity">
<TextView
android:id="@+id/text1"
android:layout_width="match_parent"
android:layout_height="100dp"
android:text="15521139529"
android:gravity="center">TextView>
<RelativeLayout
android:id="@+id/relative1"
android:layout_below="@+id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="密码"
android:layout_centerVertical="true">
TextView>
<EditText
android:id="@+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="请输入微信密码"
android:layout_toRightOf="@+id/text2"
android:gravity="center"
android:inputType="textPassword"
>EditText>
RelativeLayout>
<TextView
android:id="@+id/text3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="使用验证码登陆"
android:layout_below="@+id/relative1"
android:layout_alignParentLeft="true"
android:textColor=" #7D9EC0"
>TextView>
<Button
android:id="@+id/bottom1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="登录"
android:layout_marginTop="40dp"
android:padding="20dp"
android:layout_below="@id/text3">
Button>
<RelativeLayout
android:id="@+id/bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_alignParentBottom="true"
android:gravity="center">
<TextView
android:id="@+id/text_bottom1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="找回密码"
android:textColor=" #7D9EC0"
android:layout_toLeftOf="@+id/text_bottom2"
>TextView>
<TextView
android:id="@+id/text_bottom2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:text="紧急冻结"
android:textColor=" #7D9EC0">TextView>
<TextView
android:id="@+id/text_bottom3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="微信安全中心"
android:textColor=" #7D9EC0"
android:layout_toRightOf="@id/text_bottom2">TextView>
RelativeLayout>
RelativeLayout>
掌握一下组件的常见使用方法
private TextView mTv4;
mTv4 = findViewById(R.id.tv_4);
mTv4.getPaint().setFlags(Paint.STRIKE_THRU_TEXT_FLAG); // 添加中划线
mTv4.getPaint().setAntiAlias(true); // 去除锯齿
private TextView mTv5;
mTv5 = findViewById(R.id.tv_5);
mTv5.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG); // 添加下划线
mTv5.getPaint().setAntiAlias(true); // 去除锯齿
android:singleLine="true"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:focusable="true"
android:focusableInTouchMode="true"
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="#FFCC00"/> // 填充颜色
<corners
android:radius="10dp"/> // 圆角
shape>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<stroke
android:width="2dp"
android:color="#FFCC00"/> // 描边
<corners
android:radius="10dp"/> //圆角
shape>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"> // 按下去
<shape>
<solid android:color="#00BB25"/> // 填充颜色
<corners android:radius="10dp"/> // 圆角
shape>
item>
<item android:state_pressed="false"> // 没有按下去
<shape>
<solid android:color="#00DD2C"/>
<corners android:radius="10dp"/>
shape>
item>
selector>
android:onClick="showToast"
public void showToast(View view) {
Toast.makeText(this, "点击了一下按钮4", Toast.LENGTH_LONG).show();
}
方法二 (推荐) : 直接在 Activity 中获取控件, 新建监听事件来实现, 代码如下:
private Button mBtn3;
mBtn3 = findViewById(R.id.btn_3);
mBtn3.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(ButtonActivity.this, "点击了一下按钮3", Toast.LENGTH_LONG).show();
}
});
常用属性 | 作用 |
---|---|
hint | 提示语 |
inputType | 输入类型 |
mEditText1 = findViewById(R.id.et_1);
mEditText1.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
Log.d("useName", s.toString());
}
@Override
public void afterTextChanged(Editable s) {
}
});
掌握以下控件的常见用法:
属性 | 作用 |
---|---|
checked | 默认情况下是否选中 |
orientation | 选项摆放方向, 水平或者垂直 |
button | 设置勾选按钮的样式, 可以设置为 @null 来隐藏, 然后使用自定义的图案 |
android:button="@null"
android:background="@drawable/rb_rb_2"
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<shape>
<solid android:color="#00BB25"/>
<corners android:radius="10dp"/>
shape>
item>
<item android:state_checked="false">
<shape>
<stroke
android:width="2dp"
android:color="#00BB25"/>
<corners android:radius="10dp"/>
shape>
item>
selector>
private RadioGroup mRg1;
mRg1 = findViewById(R.id.rg_1);
mRg1.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
RadioButton radioButton = group.findViewById(checkedId);
Toast.makeText(RadioButtonActivity.this, radioButton.getText(), Toast.LENGTH_SHORT).show();
}
});
多个 CheckBox 就是一组选择几项, 即复选的 RadioGroup
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="@drawable/checkbox_checked"/>
<item android:state_checked="false" android:drawable="@drawable/checkmark_unchecked"/>
selector>
private CheckBox mCb5, mCb6;
mCb5 = findViewById(R.id.cb_21);
mCb6 = findViewById(R.id.cb_22);
mCb5.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Toast.makeText(CheckBoxActivity.this, isChecked?"checked":"unchecked", Toast.LENGTH_SHORT).show();
}
});
mCb6.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Toast.makeText(CheckBoxActivity.this, isChecked?"checked":"unchecked", Toast.LENGTH_SHORT).show();
}
});
属性 | 作用 |
---|---|
src | 图片资源的位置 |
scaleType | 图片适配视图的类型 |
scaleType值 | 效果 |
---|---|
fitXY | 拉伸长和宽, 使图片撑满整个 ImageView |
fitCenter | 等比缩放, 是图片完整展示在 ImageView 中 |
centerCrop | 等比缩放, 使图片撑满整个 ImageView |
// 加载开源包, 将对应的内容复制粘贴到 *build.gradle(Moudle:app)* 的相应位置, 然后点击同步即可
repositories {
mavenCentral()
google()
}
dependencies {
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
}
// 使用开源包, 加载网络图片
private ImageView mIv4;
mIv4 = findViewById(R.id.iv_4);
Glide.with(this).load("https://i0.hdslb.com/bfs/archive/058056424b94c3ff8c1facc940f48ce3bfe423a5.jpg@1100w_484h_1c_100q.jpg").into(mIv4);
属性 | 作用 |
---|---|
listSelector | 设置选择的效果, 可以自定义drawable文件来实现点击效果 |
divider | 设置item间的分割线的样式 |
dividerHeight | 设置分割线的宽度 |
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp">
<ImageView
android:id="@+id/iv"
android:layout_width="150dp"
android:layout_height="100dp"
android:background="@color/colorBlack"
android:scaleType="fitXY"/>
<LinearLayout
android:id="@+id/ll"
android:layout_width="match_parent"
android:layout_height="100dp"
android:orientation="vertical"
android:paddingLeft="10dp">
<TextView
android:id="@+id/tv_tittle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="橡皮擦"
android:textSize="20sp"
android:textColor="@color/colorBlack"/>
<TextView
android:id="@+id/tv_date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="2020-02-07"
android:textSize="18sp"
android:textColor="@color/colorGray"
android:layout_marginTop="5dp"/>
<TextView
android:id="@+id/tv_details"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="这是介绍, 橡皮擦擦铅笔迹!"
android:textSize="19sp"
android:layout_marginTop="5dp"
android:textColor="@color/colorGray"/>
LinearLayout>
LinearLayout>
public class MyListAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mLayoutInflater;
public MyListAdapter(Context context) {
this.mContext = context;
mLayoutInflater = LayoutInflater.from(context); // 获取布局填充器
}
@Override
public int getCount() {
return 10;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
static class ViewHolder {
public ImageView imageView;
public TextView tvTittle, tvDate, tvDetails;
}
@Override
// 列表每一行长什么样子, 这个方法可以得到
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) { // 如果当前 item 是空的, 则新建一个convertView, 更新viewHolder, 并放到缓存区
// convertView 是一个item View, 将R.layout.layout_list_item填充到一个view得到convertView
convertView = mLayoutInflater.inflate(R.layout.layout_list_item, null);
// viewHolder 是管理一个 item 上的所有控件的类对象
viewHolder = new ViewHolder();
viewHolder.imageView = convertView.findViewById(R.id.iv);
viewHolder.tvTittle = convertView.findViewById(R.id.tv_tittle);
viewHolder.tvDate = convertView.findViewById(R.id.tv_date);
viewHolder.tvDetails = convertView.findViewById(R.id.tv_details);
// 将 viewHolder 放到缓存区
convertView.setTag(viewHolder);
} else { // 如果当前 item 不是空的, 则直接从缓存区取来就好
viewHolder = (ViewHolder)convertView.getTag();
}
// 给控件赋值, 会覆盖 xml 文件中的初始值
viewHolder.tvTittle.setText("这是标题");
viewHolder.tvDate.setText("2022-02-22");
viewHolder.tvDetails.setText("这是内容喔!");
Glide.with(mContext).load("https://i0.hdslb.com/bfs/archive/058056424b94c3ff8c1facc940f48ce3bfe423a5.jpg@1100w_484h_1c_100q.jpg").into(viewHolder.imageView);
return convertView;
}
}
private ListView mLv1;
mLv1 = findViewById(R.id.lv_1);
mLv1.setAdapter(new MyListAdapter(ListViewActivity.this));
mLv1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(ListViewActivity.this, "position" + position, Toast.LENGTH_SHORT).show();
}
});
mLv1.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(ListViewActivity.this, "position" + position, Toast.LENGTH_SHORT).show();
return true;
}
});
也有 ListView 的许多属性, 还有以下这些:
属性 | 作用 |
---|---|
numColumns | 多少列 |
horizontalSpacing | 水平间距 |
verticalSpacing | 竖直间距 |
同 ListView , 代码如下:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp">
<ImageView
android:id="@+id/iv_image"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="@color/colorPink"
android:scaleType="centerCrop"/>
<TextView
android:id="@+id/tv_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="name"
android:gravity="center"/>
LinearLayout>
public class MyGridViewAdapter extends BaseAdapter {
private Context mContext;
private LayoutInflater mLayoutInflater;
public MyGridViewAdapter(Context context){
this.mContext = context;
mLayoutInflater = LayoutInflater.from(context);
}
static class ViewHolder{
public ImageView mImageView;
public TextView mTextView;
}
@Override
public int getCount() {
return 10;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = mLayoutInflater.inflate(R.layout.layout_grid_item, null);
viewHolder = new ViewHolder();
viewHolder.mImageView = convertView.findViewById(R.id.iv_image);
viewHolder.mTextView = convertView.findViewById(R.id.tv_name);
convertView.setTag(viewHolder);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.mTextView.setText("JoJo");
Glide.with(mContext).load("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1581092772754&di=252dc3a97008701c0c654df1cb070f42&imgtype=0&src=http%3A%2F%2Fi2.hdslb.com%2Fbfs%2Farchive%2F23cd383165745a6fdc4ddc06ecc31e5cee91f91c.jpg").into(viewHolder.mImageView);
return convertView;
}
}
private GridView mGv1
mGv1 = findViewById(R.id.gv);
mGv1.setAdapter(new MyGridViewAdapter(GridViewActivity.this));
// 单次点击
mGv1.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(GridViewActivity.this, "点击了图片" + position, Toast.LENGTH_SHORT).show();
}
});
// 长按
mGv1.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(GridViewActivity.this, "长按了图片" + position, Toast.LENGTH_SHORT).show();
return true;
}
});
当控件太多, 手机屏幕放不下时, 就可以使用 ScrollView 来实现滚动功能
将需要滚动的布局放在 ScrollView 里面就好, 注意里面只能放一个元素, 竖直滚动用 ScrollView , 水平滚动用 HorizontalScrollView
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ScrollViewActivity">
<LinearLayout
android:id="@+id/sv_ll_1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="5dp">
<TextView
android:layout_width="match_parent"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"/>
<TextView
android:layout_width="match_parent"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"
android:layout_marginTop="10dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"
android:layout_marginTop="10dp"/>
<TextView
android:layout_width="match_parent"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"
android:layout_marginTop="10dp"/>
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:layout_width="200dp"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"
android:layout_marginTop="10dp"/>
<TextView
android:layout_width="200dp"
android:layout_height="150dp"
android:layout_marginTop="10dp"
android:background="@color/colorPink"
android:gravity="center"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:layout_marginLeft="10dp"/>
<TextView
android:layout_width="200dp"
android:layout_height="150dp"
android:text="\(@^0^@)/"
android:textSize="30sp"
android:gravity="center"
android:background="@color/colorPink"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"/>
LinearLayout>
HorizontalScrollView>
LinearLayout>
ScrollView>
RecycleView
三种方法
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(HorRecyclerViewActivity.this); // 创建布局管理器对象
mRv.setLayoutManager(linearLayoutManager); // 为RecyclerView控件设置布局管理器
mRvHor.setAdapter(new HorAdapter(HorRecyclerViewActivity.this, new HorAdapter.OnItemClickListener() {
@Override
public void onClick(int pos) {
Toast.makeText(HorRecyclerViewActivity.this, "点击了" + pos, Toast.LENGTH_SHORT).show();
}
}));
// activity的布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".recyclerview.RecyclerViewActivity"
android:layout_margin="10dp">
<Button
android:id="@+id/btn_linear"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="列表视图"/>
LinearLayout>
// item布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/tv_tittle"
android:layout_width="match_parent"
android:layout_height="50dp"
android:textSize="20sp"
android:textColor="@color/colorBlack"
android:gravity="center"
android:background="@color/colorWhite"/>
LinearLayout>
public class LinearAdapter extends RecyclerView.Adapter<LinearAdapter.LinearViewHolder> {
private Context mContext;
public LinearAdapter(Context context) {
this.mContext = context;
}
@NonNull
@Override
/**
* 创建 ViewHolder, 是用来实现复用功能的
*/
public LinearAdapter.LinearViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new LinearViewHolder(LayoutInflater.from(mContext).inflate(R.layout.layout_linear_item, parent, false));
}
@Override
/**
* 是用来设置数据的,当item划入屏幕时,就会调用此方法,来改变数据
*/
public void onBindViewHolder(@NonNull LinearAdapter.LinearViewHolder holder, final int position) {
holder.mTv.setText("设置holder的值");
}
@Override
/**
* 是用来获取item个数的,实际开发是要获取List的长度
*/
public int getItemCount() {
return 30;
}
/**
* 自定义ViewHolder的类型,并将此类型代替所有的泛型
*/
class LinearViewHolder extends RecyclerView.ViewHolder {
private TextView mTv;
public LinearViewHolder(@NonNull View itemView) {
super(itemView);
mTv = itemView.findViewById(R.id.tv_tittle);
}
}
}
public class LinearRecyclerViewActivity extends AppCompatActivity {
private RecyclerView mRvMain;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear_recycler_view);
mRvMain = findViewById(R.id.rv_main);
mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerViewActivity.this)); // 设置布局管理器, 有三种线性、表格和瀑布流
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.this);
}
}
使用 addItemDecoration 来实现分割线
public class LinearRecyclerViewActivity extends AppCompatActivity {
private RecyclerView mRvMain;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear_recycler_view);
mRvMain = findViewById(R.id.rv_main);
mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerViewActivity.this)); // 设置布局管理器, 有三种线性、表格和瀑布流
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.thi);
mRvMain.addItemDecoration(new MyDecoration());
}
/**
* 实现分割线的作用
*/
class MyDecoration extends RecyclerView.ItemDecoration {
@Override
public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
outRect.set(0, 0, 0, getResources().getDimensionPixelOffset(R.dimen.dividerHeight));
}
}
}
@Override
/**
* 是用来设置数据的,当item划入屏幕时,就会调用此方法,来改变数据或设置监听(第一种方法)
*/
public void onBindViewHolder(@NonNull LinearAdapter.LinearViewHolder holder, final int position) {
holder.mTv.setText("设置holder的值");
holder.mTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(mContext, "点击了" + position, Toast.LENGTH_SHORT).show();
}
});
}
// 适配器的代码
public class LinearAdapter extends RecyclerView.Adapter<LinearAdapter.LinearViewHolder> {
private Context mContext;
private OnItemClickListener mOnItemClickListener; // 声明接口
public LinearAdapter(Context context, OnItemClickListener onItemClickListener) {
this.mContext = context;
this.mOnItemClickListener = onItemClickListener; // 初始化接口
}
@NonNull
@Override
/**
* 创建 ViewHolder, 是用来复用的
*/
public LinearAdapter.LinearViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new LinearViewHolder(LayoutInflater.from(mContext).inflate(R.layout.layout_linear_item, parent, false));
}
@Override
/**
* 是用来设置数据的,当item划入屏幕时,就会调用此方法,来改变数据或设置监听(第一种方法)
*/
public void onBindViewHolder(@NonNull LinearAdapter.LinearViewHolder holder, final int position) {
holder.mTv.setText("设置holder的值");
holder.mTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 接口方法的调用,实例化后重写该方法,即最终重写后的方法在此处调用
mOnItemClickListener.onClick(position);
}
});
}
@Override
/**
* 是用来获取item个数的,实际开发是要获取List的长度
*/
public int getItemCount() {
return 30;
}
/**
* 自定义ViewHolder的类型,并将此类型代替所有的泛型
*/
class LinearViewHolder extends RecyclerView.ViewHolder {
private TextView mTv;
public LinearViewHolder(@NonNull View itemView) {
super(itemView);
mTv = itemView.findViewById(R.id.tv_tittle);
}
}
/**
* 写一个接口实现第二种设置监听的方法
* 即实例化这个类,就得实现这个接口,重写方法,而该方法又是在onBindViewHolder()方法中调用的。
* 如此一来,只要重写方法中添加一个监听事件就可以实现,但item出现在屏幕时,就可以为该item添加一个监听事件。
*/
public interface OnItemClickListener {
void onClick(int pos);
}
}
// activity的代码
public class LinearRecyclerViewActivity extends AppCompatActivity {
private RecyclerView mRvMain;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_linear_recycler_view);
mRvMain = findViewById(R.id.rv_main);
mRvMain.setLayoutManager(new LinearLayoutManager(LinearRecyclerViewActivity.this)); // 设置布局管理器, 有三种线性、表格和瀑布流
mRvMain.setAdapter(new LinearAdapter(LinearRecyclerViewActivity.this, new LinearAdapter.OnItemClickListener() {
@Override
public void onClick(int pos) {
Toast.makeText(LinearRecyclerViewActivity.this, "点击了" + pos, Toast.LENGTH_SHORT).show();
}
}));
}
}
只需要设置线性布局的方向就好了
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(HorRecyclerViewActivity.this);
linearLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); // 设置滚动方向
mRvHor.setLayoutManager(linearLayoutManager);
mRvHor.setAdapter(new HorAdapter(HorRecyclerViewActivity.this, new HorAdapter.OnItemClickListener() {
@Override
public void onClick(int pos) {
Toast.makeText(HorRecyclerViewActivity.this, "点击了" + pos, Toast.LENGTH_SHORT).show();
}
}));
得使用表格布局管理器 GridLayoutManager
new GridLayoutManager(GridRecyclerViewActivity.this, 3);
public class GridRecyclerViewActivity extends AppCompatActivity {
private RecyclerView mRvGrid;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grid_recycler_view);
mRvGrid = findViewById(R.id.rv_grid);
GridLayoutManager gridLayoutManager = new GridLayoutManager(GridRecyclerViewActivity.this, 3); // 3列
mRvGrid.setLayoutManager(gridLayoutManager);
mRvGrid.setAdapter(new GridAdapter(GridRecyclerViewActivity.this, new GridAdapter.OnItemClickListener() {
@Override
public void onClick(int pos) {
Toast.makeText(GridRecyclerViewActivity.this, "点击了" + pos, Toast.LENGTH_SHORT).show();
}
}));
}
}
得使用表格布局管理器 StaggeredGridLayoutManager
new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
public class StaggeredRecyclerViewActivity extends AppCompatActivity {
private RecyclerView mRvSta;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_staggered_recycler_view);
mRvSta = findViewById(R.id.rv_staggered);
StaggeredGridLayoutManager staggeredGridLayoutManager = new StaggeredGridLayoutManager(2,StaggeredGridLayoutManager.VERTICAL);
mRvSta.setLayoutManager(staggeredGridLayoutManager);
mRvSta.setAdapter(new StaggeredAdapter(StaggeredRecyclerViewActivity.this, new StaggeredAdapter.OnItemClickListener() {
@Override
public void onClick(int pos) {
Toast.makeText(StaggeredRecyclerViewActivity.this, "点击了" + pos, Toast.LENGTH_SHORT).show();
}
}));
mRvSta.addItemDecoration(new MyItemDecoration());
}
}
就是自定义Adapter的ViewHolder要求多种类型,可以通过重写getItemViewType(int position)方法,根据不同的信息返回不同的item类型。接下来ViewHolder的创建和onBindViewHolder方法中的具体操作都根据item类型来操作。
public class MulVHAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private OnItemClickListener mOnItemClickListener;
public MulVHAdapter(Context context, OnItemClickListener onItemClickListener) {
this.mContext = context;
this.mOnItemClickListener = onItemClickListener;
}
@Override
/**
* 根据iitem的位置,判断item的类型
* return 0: 奇数, item是Text类型
* return 1: 偶数,item是Iamge类型
*/
public int getItemViewType(int position) {
int flag;
if(position % 2 == 0){
flag = 1;
} else {
flag = 0;
}
return flag;
}
@NonNull
@Override
/**
* 根据item的类型,创建对应的ViewHolder
*/
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
RecyclerView.ViewHolder viewHolder = null;
if(viewType == 0) { // Text类型
viewHolder = new MyViewHolderText(LayoutInflater.from(mContext).inflate(R.layout.layout_mvh_text_item, parent, false));
} else { // Image类型
viewHolder = new MyViewHolderImage(LayoutInflater.from(mContext).inflate(R.layout.layout_mvh_image_item, parent, false));
}
return viewHolder;
}
@Override
/**
* 根据item的类型,进行不同的操作
*/
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, final int position) {
if(getItemViewType(position) == 0) {
((MyViewHolderText)holder).mTv.setText("我是文本");
((MyViewHolderText)holder).mTv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onClick(position);
}
});
} else {
((MyViewHolderImage)holder).mIv.setImageResource(R.drawable.pikaqiu);
((MyViewHolderImage)holder).mIv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onClick(position);
}
});
}
}
@Override
/**
* 是用来获取item个数的,实际开发是要获取List的长度
*/
public int getItemCount() {
return 30;
}
/**
* 自定义ViewHolder的类型1, Text类型
*/
class MyViewHolderText extends RecyclerView.ViewHolder {
private TextView mTv;
public MyViewHolderText(@NonNull View itemView) {
super(itemView);
mTv = itemView.findViewById(R.id.tv_mvh);
}
}
/**
* 自定义ViewHolder的类型2,Image类型
*/
class MyViewHolderImage extends RecyclerView.ViewHolder {
private ImageView mIv;
public MyViewHolderImage(@NonNull View itemView) {
super(itemView);
mIv = itemView.findViewById(R.id.iv_mvh);
}
}
/**
* 写一个接口实现第二种设置监听的方法
* 即实例化这个类,就得实现这个接口。如此一来,就可以
*/
public interface OnItemClickListener {
void onClick(int pos);
}
}
加载网页
需要在main文件夹下新建一个assete文件夹,把html文件放在这个文件夹下,使用以下代码进行加载。
mWvMain.loadUrl("file:///android_asset/test.html");
分两步走:
mWvMain.getSettings().setJavaScriptEnabled(true);
mWvMain.loadUrl("https://m.baidu.com");
用于处理各种通知、请求事件。为一个webView设置 WebViewClient mWvMain.setWebViewClient(new MyWebViewClient());
其中,MyWebViewClient() 是自定义的继承于 WebViewClient 的类,重写以下方法:
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return false;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.d("WebView", "Page Started");
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.d("WebView", "Page Finished");
}
同 WebViewClient , 只不过 WebChromClient 是处理对话框、网站图标、网站title、加载进度等。
为一个 WebView 设置一个WebChromClient: mWvMain.setWebChromeClient(new MyWebChromeClient());
public class MyWebChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
setTitle(title);
}
}
进入URL界面时,若不对返回键进行设置,则返回键会直接退出URL界面。实现返回键为回到上一个URL界面,秩序重写监听方法 onKeyDown(int keyCode, KeyEvent event)
即可,代码如下:
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && mWvMain.canGoBack()) {
mWvMain.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
public class WebViewActivity extends AppCompatActivity {
private WebView mWvMain;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_web_view);
mWvMain = findViewById(R.id.wv);
// 加载本地html文件
// mWvMain.loadUrl("file:///android_asset/test.html");
// 加载网络URL
mWvMain.getSettings().setJavaScriptEnabled(true); // 设置支持
mWvMain.setWebViewClient(new MyWebViewClient()); // 处理各种通知、请求事件
mWvMain.setWebChromeClient(new MyWebChromeClient()); // 处理对话框、网站图标、网站title、加载进度等
mWvMain.loadUrl("https://m.baidu.com"); //
}
public class MyWebViewClient extends WebViewClient {
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
/**
* 返回true终止加载URL,返回false在WebView中加载URL
*/
@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return false;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
Log.d("WebView", "Page Started");
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
Log.d("WebView", "Page Finished");
}
}
public class MyWebChromeClient extends WebChromeClient {
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
setTitle(title);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && mWvMain.canGoBack()) {
mWvMain.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}
是一个消息弹窗提示组件
Toast.makeText(getApplicationContext(), "Toast", Toast.LENGTH_SHORT).show();
toastCenter.setGravity(Gravity.CENTER, 0, 0);
toastCustom.setView(view);
switch (v.getId()){
case R.id.btn_toast_1: // 默认情况
Toast.makeText(getApplicationContext(), "Toast", Toast.LENGTH_SHORT).show();
break;
case R.id.btn_toast_2: // 调整位置
Toast toastCenter = Toast.makeText(getApplicationContext(), "Toast", Toast.LENGTH_SHORT);
toastCenter.setGravity(Gravity.CENTER, 0, 0);
toastCenter.show(); ;
case R.id.btn_toast_3: // 自定义图案
Toast toastCustom = new Toast(ToastActivity.this);
View view = LayoutInflater.from(ToastActivity.this).inflate(R.layout.layout_toast_item, null);
ImageView imageView = view.findViewById(R.id.iv_toast);
TextView textView = view.findViewById(R.id.tv_toast);
imageView.setImageResource(R.drawable.icon_smile);
textView.setText("我是一个Toast");
toastCustom.setView(view);
toastCustom.setDuration(Toast.LENGTH_SHORT);
toastCustom.show();
break;
}
弹出一个消息对话框. 使用构建器来管理, 具体方法可以看构建器里面的方法介绍, 新建构建器: AlertDialog.Builder builder5 = new AlertDialog.Builder(DialogActivity.this);
实现5种情况的对话框
switch (v.getId()) {
case R.id.btn_dialog_1:
AlertDialog.Builder builder1 = new AlertDialog.Builder(DialogActivity.this);
builder1.setTitle("请回答").setMessage("我帅不帅?").setIcon(R.drawable.icon_smile)
.setPositiveButton("帅呆了", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, "有眼光", Toast.LENGTH_SHORT).show();
}
}).setNegativeButton("一般", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, "Fuck", Toast.LENGTH_SHORT).show();
}
}).setNeutralButton("还行", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, "你再想想", Toast.LENGTH_SHORT).show();
}
}).show();
break;
case R.id.btn_dialog_2:
final String[] array2 = new String[]{"boy", "girl"};
AlertDialog.Builder builder2 = new AlertDialog.Builder(DialogActivity.this);
builder2.setTitle("What's your sex?").setItems(array2, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, array2[which], Toast.LENGTH_SHORT).show();
}
}).show();
break;
case R.id.btn_dialog_3: // 单选
final String[] array3 = new String[]{"boy", "girl"};
AlertDialog.Builder builder3 = new AlertDialog.Builder(DialogActivity.this);
builder3.setTitle("What's your sex?").setSingleChoiceItems(array3, 1, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, array3[which], Toast.LENGTH_SHORT).show();
}
}).show();
break;
case R.id.btn_dialog_4: // 多选
final String[] array4 = new String[]{"boy", "girl", "I don't know!"};
boolean[] isSelected = new boolean[]{false, false, true};
AlertDialog.Builder builder4 = new AlertDialog.Builder(DialogActivity.this);
builder4.setTitle("What's your sex?").setMultiChoiceItems(array4, isSelected, new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked) {
Toast.makeText(DialogActivity.this, array4[which], Toast.LENGTH_SHORT).show();
}
}).setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, "确定", Toast.LENGTH_SHORT).show();
}
}).setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Toast.makeText(DialogActivity.this, "取消", Toast.LENGTH_SHORT).show();
}
}).show();
break;
case R.id.btn_dialog_5: // 自定义View
AlertDialog.Builder builder5 = new AlertDialog.Builder(DialogActivity.this);
View view = LayoutInflater.from(DialogActivity.this).inflate(R.layout.layout_dialog, null);
builder5.setView(view).setTitle("请先登录").show();
break;
}
前者是进度条, 后者是进度对话框, 在API26就已经弃用了, 这里不学了, 也挺简单的
下面列举出几种样式的进度条, 也可以 自定义样式, 这里不说自定义样式了, 需要时候再搜索就好.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:layout_margin="10dp">
<ProgressBar
android:id="@+id/rb_def"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="visible"/>
<ProgressBar
android:id="@+id/rb_style_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
style="@android:style/Widget.ProgressBar"
android:layout_marginTop="10dp"/>
<ProgressBar
android:id="@+id/rb_style_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/Widget.ProgressBar.Horizontal"
android:progress="10"
android:secondaryProgress="30"
android:layout_marginTop="10dp"/>
<ProgressBar
android:id="@+id/rb_style_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/Widget.Holo.ProgressBar.Horizontal"
android:progress="20"
android:secondaryProgress="30"
android:layout_marginTop="10dp"/>
<ProgressBar
android:id="@+id/rb_style_4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@android:style/Widget.DeviceDefault.ProgressBar.Horizontal"
android:progress="20"
android:secondaryProgress="30"
android:layout_marginTop="10dp"/>
LinearLayout>
写都写了, 还是将代码附上来吧
mBtnProgressDialog1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
ProgressDialog progressDialog = new ProgressDialog(ProgressActivity.this);
progressDialog.setTitle("请稍等");
progressDialog.setMessage("正在加载");
progressDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
Toast.makeText(ProgressActivity.this, "加载完成", Toast.LENGTH_SHORT).show();
}
});
progressDialog.setCancelable(false); // 不允许返回, 当加载完成时才能可以调用progressDialog.setCancelable(true); 来取消加载
progressDialog.setCancelable(true);
progressDialog.show();
}
});
自定义类 CustomDialog 代码如下:
public class CustomDialog extends Dialog implements View.OnClickListener{
private TextView mTvTittle, mTvMassage, mTvPositive, mTvNegative;
private String tittle, massage, positive, negative;
private IOnNegativeListener negativeListener;
private IOnPositiveListener positiveListener;
public CustomDialog(@NonNull Context context) {
super(context);
}
public CustomDialog(@NonNull Context context, int themeResId) {
super(context, themeResId);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout_custom_dialog);
mTvTittle = findViewById(R.id.tv_tittle);
mTvMassage = findViewById(R.id.tv_massage);
mTvPositive = findViewById(R.id.tv_positive);
mTvNegative = findViewById(R.id.tv_negative);
if (!TextUtils.isEmpty(tittle)) {
mTvTittle.setText(tittle);
}
if (!TextUtils.isEmpty(massage)) {
mTvMassage.setText(massage);
}
if (!TextUtils.isEmpty(positive)) {
mTvPositive.setText(positive);
}
if (!TextUtils.isEmpty(negative)) {
mTvNegative.setText(negative);
}
mTvPositive.setOnClickListener(this);
mTvNegative.setOnClickListener(this);
}
public String getTittle() {
return tittle;
}
public CustomDialog setTittle(String tittle) {
this.tittle = tittle;
return this;
}
public String getMassage() {
return massage;
}
public CustomDialog setMassage(String massage) {
this.massage = massage;
return this;
}
public String getPositive() {
return positive;
}
public CustomDialog setPositive(String positive, IOnPositiveListener positiveListener) {
this.positive = positive;
this.positiveListener = positiveListener;
return this;
}
public String getNegative() {
return negative;
}
public CustomDialog setNegative(String negative, IOnNegativeListener negativeListener) {
this.negative = negative;
this.negativeListener = negativeListener;
return this;
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.tv_positive:
positiveListener.positive(this);
break;
case R.id.tv_negative:
negativeListener.negative(this);
break;
}
}
public interface IOnPositiveListener {
void positive(CustomDialog customDialog);
}
public interface IOnNegativeListener {
void negative(CustomDialog customDialog);
}
}
布局文件的代码
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="@drawable/layout_dialog">
<TextView
android:id="@+id/tv_tittle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="提示"
android:textSize="20sp"
android:gravity="center"
android:textColor="@color/colorGray"
android:layout_margin="20dp"/>
<TextView
android:id="@+id/tv_massage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="确定删除?"
android:textSize="20sp"
android:gravity="center"
android:textColor="@color/colorBlack"
android:layout_margin="20dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorLightGray"
android:layout_marginTop="20dp"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_positive"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="确定"
android:textSize="20sp"
android:textColor="@color/colorLightGrayishBlue"
android:gravity="center"
android:layout_weight="1"/>
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="@color/colorLightGray"/>
<TextView
android:id="@+id/tv_negative"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="取消"
android:textSize="20dp"
android:textColor="@color/colorLightGrayishBlue"
android:gravity="center"
android:layout_weight="1"/>
LinearLayout>
LinearLayout>
使用自定义类 CustomDialog
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_dialog);
mBtnCustomDialog1 = findViewById(R.id.btn_custom_dialog_1);
mBtnCustomDialog1.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
CustomDialog customDialog = new CustomDialog(CustomDialogActivity.this);
customDialog.setTittle("提示").setMassage("你确定删除吗?").setPositive("确定", new CustomDialog.IOnPositiveListener() {
@Override
public void positive(CustomDialog customDialog) {
}
}).setNegative("取消", new CustomDialog.IOnNegativeListener() {
@Override
public void negative(CustomDialog customDialog) {
}
}).show();
}
});
}
它是一个容器, 可以把View放到里面去, 通过设计监听, 可以点弹出View
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_popup_window);
mBtnPop = findViewById(R.id.btn_pop);
mBtnPop.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
View view = getLayoutInflater().inflate(R.layout.layout_pop, null);
mPop = new PopupWindow(view, mBtnPop.getWidth(), ViewGroup.LayoutParams.WRAP_CONTENT);
mPop.setOutsideTouchable(true);
mPop.setFocusable(true);
// mPop.setAnimationStyle();
mPop.showAsDropDown(mBtnPop);
final TextView textView = view.findViewById(R.id.tv_1);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(PopupWindowActivity.this, textView.getText(), Toast.LENGTH_SHORT).show();
mPop.dismiss();
//do something
}
});
}
});
}
布局文件
// R.layout.layout_pop 布局文件
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_margin="5dp">
<TextView
android:id="@+id/tv_1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="苹果"
android:gravity="center"
android:padding="5dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorGray"/>
<TextView
android:id="@+id/tv_2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="雪梨"
android:gravity="center"
android:padding="5dp"/>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/colorGray"/>
<TextView
android:id="@+id/tv_3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="香蕉"
android:gravity="center"
android:padding="5dp"/>
LinearLayout>
Activity主要是用来做控制的,它可以选择要显示的View,也可以从View中获取数据然后把数据传给Model层进行处理,最后再来显示出处理结果。
public class UIActivity extends AppCompatActivity { ...
setContentView(R.layout.activity_ui);
(通常情况下得需要这一步, 但并非必须的)属性 | 作用 |
---|---|
label | 设置最顶端一栏, 也就是action bar 的文本内容 |
theme | 设置主题, 可以在 application 标签内设置, 这样就会运用到全部的activity中去 |
screenOrientation | 方向, 竖屏或者横屏, 或者全屏 |
intent-filter | 这个标签可以设置启动项 |
打印日志, 查看生命周期如何运行的
public class LifeCycleActivity extends AppCompatActivity {
private String eTag = "LifeCycle";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_life_cycle);
Log.e(eTag, "onCreate");
}
@Override
protected void onStart() {
super.onStart();
Log.e(eTag, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.e(eTag, "onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.e(eTag, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.e(eTag, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.e(eTag, "onDestroy");
}
}
一般用显式1就好了, 其他的要看得懂
// 方式一
// 第一个参数: 当前Activity. 第二个参数: 目标 Activity
Intent intent = new Intent(JumpActivity.this, TargetActivity.class);
startActivity(intent);
// 方式二
Intent intent = new Intent();
intent.setClass(JumpActivity.this, TargetActivity.class);
startActivity(intent);
// 方式三
Intent intent = new Intent();
intent.setClassName(JumpActivity.this, "com.example.learningandroid.activity.jump.TargetActivity");
startActivity(intent);
// 方式四
Intent intent = new Intent();
intent.setComponent(new ComponentName(JumpActivity.this, "com.example.learningandroid.activity.jump.TargetActivity"));
startActivity(intent);
需要在 AndroidManifest 文件的目标 Activity内增加一个标签, 具体如下:
// 第一个字符串随便起, 对应好就行, 第二个字符串得是DEFAULT
<intent-filter>
<action android:name="android.intent.action.jumpto" />
<category android:name="android.intent.category.DEFAULT" />
intent-filter>
然后就可以使用显式跳转了
Intent intent = new Intent();
intent.setAction("android.intent.action.jumpto");
startActivity(intent);
Intent intent = new Intent(JumpActivity.this, TargetActivity.class);
Bundle bundle = new Bundle();
bundle.putString("name", "阿笑");
bundle.putInt("old", 18);
intent.putExtras(bundle);
Bundle bundle = getIntent().getExtras();
mTv.setText(bundle.getString("name") + bundle.getInt("old"));
// 当前Activity中:
// 跳转 *Activity* 要用这个方法了
startActivityForResult(intent, 0);
// 同时要重写onActivityResult()方法以获取传回来的数据, 数据在data种
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
Toast.makeText(JumpActivity.this, data.getExtras().getString("content"), Toast.LENGTH_LONG).show();
}
// 目标Activity中:
// 设置该按钮, 点击后回到原来的 *Activity*, 并调用 *setResult()* 方法传回数据, 和 *finish()* 方法关闭当前的 *Activity*
mBtnBack.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent();
Bundle bundle1 = new Bundle();
bundle1.putString("content", "I'm back!");
intent.putExtras(bundle1);
setResult(Activity.RESULT_OK, intent);
finish();
}
});
Android 的 Activity 是由栈管理的, 每启动一个 Activity 就会被放进栈种, 按返回键, 就会从栈顶移除一个 Activity .
在 AndroidManifest 文件种可以设置启动模式: android:launchMode
, 可以设置任务栈名称: android:taskAffinity
, 这相当于一个新的任务栈了, 这之后的都往这里放了.
有四种启动模式:可以参考 图解4种启动模式
碎片(Fragment)是一种可以嵌入在活动当中的UI片段,它能让程序更加合理和充分地利用大屏幕的空间,因而在平板上应用的非常广泛。虽然碎片对你来说应该是个全新的概念,但我相信你学习起来应该毫不费力,因为它和活动实在是太像了,同样都能包含布局,同样都有自己的生命周期。你甚至可以将碎片理解成一个迷你型的活动,虽然这个迷你型的活动有可能和普通的活动是一样大的。
emmm…觉得会用不上, 就先不学了
要点: 监听器的创建由许多种方式, 可以直接匿名内部类, 也可以写个类, 再实例化它来使用, 还可以让事件源所在的类实现监听接口, 然后直接用本类来创建监听器, 更是可以通过外部自定义监听器创建的类, 然后实例它来实现, 所以就由以下4种方法来设置事件监听
public class EventActivity extends AppCompatActivity implements View.OnClickListener {
private Button mBtnToast;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_event);
mBtnToast = findViewById(R.id.btn_toast);
// 方式一: 内部类
// mBtnToast.setOnClickListener(new OnClick());
// 方式二: 匿名内部类
// mBtnToast.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
// Toast.makeText(EventActivity.this, "匿名内部类实现事件监听", Toast.LENGTH_SHORT).show();
// }
// });
// 方式三: 通过事件源所在的类来实现事件监听
// mBtnToast.setOnClickListener(EventActivity.this);
// 方式四: 通过外部类来实现
mBtnToast.setOnClickListener(new MyListener(this));
}
@Override
public void onClick(View v) {
Toast.makeText(EventActivity.this, "通过事件源所在的类来实现事件监听", Toast.LENGTH_SHORT).show();
}
private class OnClick implements View.OnClickListener {
@Override
public void onClick(View v) {
Toast.makeText(EventActivity.this, "内部类实现事件监听", Toast.LENGTH_SHORT).show();
}
}
}
public class MyListener implements View.OnClickListener {
private Activity activity;
public MyListener(Activity activity) {
this.activity = activity;
}
@Override
public void onClick(View v) {
Toast.makeText(this.activity, "通过外部类来实现", Toast.LENGTH_SHORT).show();
}
}
当然, 还有第五种方式, 也就是通过布局文件设置 android:onClick=“方法名” ,并在类中实现这个方法, 注意访问限制符是 public , 参数是也可以实现
android:onClick="show"
public void show(View v) {
Toast.makeText(EventActivity.this, "通过布局文件来实现", Toast.LENGTH_SHORT).show();
}
注意 : 监听器最终起作用的是最后建立的.
利用 Handler 实现需求 : 在某一 Activity 停留3秒后跳转到另外一个 Activity
mHandler = new Handler();
mHandler.postDelayed(new Runnable() {
@Override
public void run() {
Intent intent = new Intent();
intent.setClass(HandlerActivity.this, ButtonActivity.class);
startActivity(intent);
}
}, 3000);
Handler 可以实现上述功能, 但一般不拿来这么用, 一般会结合新建的线程, 进行线程之间的通信:
mHandler = new Handler() { // 主线程
@Override
public void handleMessage(@NonNull Message msg) {
super.handleMessage(msg);
switch (msg.what) { // 根据不同的消息进行不一样的操作
case 1 :
Toast.makeText(HandlerActivity.this, "线程通讯成功", Toast.LENGTH_SHORT).show();
break;
default :
}
}
};
// 新建一个线程
new Thread(){
@Override
public void run() {
super.run();
Message message = new Message();
message.what = 1; // 消息识别标识
mHandler.sendMessage(message); // Handler发送消息
}
}.start();
mEditor.putString("content", mEtInputContent.getText().toString()); mEditor.apply();
mSharedPreferences.getString("content", "no content")
public class SharedPreferencesActivity extends AppCompatActivity {
private EditText mEtInputContent;
private Button mBtnSave, mBtnShow;
private TextView mTvShow;
private SharedPreferences mSharedPreferences; // 用来读轻量的数据
private SharedPreferences.Editor mEditor; // 用来写轻量的数据
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_shared_preferences);
mEtInputContent = findViewById(R.id.et_input_content);
mBtnSave = findViewById(R.id.btn_save_data);
mBtnShow = findViewById(R.id.btn_show_data);
mTvShow = findViewById(R.id.tv_show_content);
mSharedPreferences = getSharedPreferences("data", MODE_PRIVATE);
mEditor = mSharedPreferences.edit();
mBtnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// putString(), 键值对的形式存数据
mEditor.putString("content", mEtInputContent.getText().toString());
mEditor.apply();
}
});
mBtnShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// getString(), 取数据
mTvShow.setText(mSharedPreferences.getString("content", "no content"));
}
});
}
}
参考资料 : 五大数据存储
fileOutputStream = openFileOutput(mFILENAME, MODE_PRIVATE)
fileOutputStream.write(content.getBytes())
fileOutputStream.close();
fileInputStream = openFileInput(mFILENAME);
fileInputStream.read(buff)
fileInputStream.close();
public class FileActivity extends AppCompatActivity {
private EditText mEtInputContent;
private Button mBtnSave, mBtnShow;
private TextView mTvShow;
private final String mFILENAME = "test.txt";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file);
mEtInputContent = findViewById(R.id.et_input_content);
mBtnSave = findViewById(R.id.btn_save_data);
mBtnShow = findViewById(R.id.btn_show_data);
mTvShow = findViewById(R.id.tv_show_content);
mBtnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
save(mEtInputContent.getText().toString());
}
});
mBtnShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTvShow.setText(read());
}
});
}
// 存储数据
private void save(String content) {
FileOutputStream fileOutputStream = null;
try {
fileOutputStream = openFileOutput(mFILENAME, MODE_PRIVATE);
fileOutputStream.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 读取数据
private String read() {
FileInputStream fileInputStream = null;
try {
fileInputStream = openFileInput(mFILENAME);
byte[] buff = new byte[1024];
StringBuilder stringBuilder = new StringBuilder(""); // 一个可变长字符串
int len = 0;
while ((len = fileInputStream.read(buff)) > 0) {
stringBuilder.append(new String(buff, 0, len));
}
return stringBuilder.toString();
}catch (IOException e) {
e.printStackTrace();
} finally {
if (fileInputStream != null) {
try {
fileInputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
外部存储的访问需要设置权限以及动态申请权限, 首先得再 AndroidManifest 文件中申请权限 :
, 然后得动态申请用户的权限:
String[] permissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
List<String> mUnPermissionList = new ArrayList<>();
// private ImageView welcomeImg = null;
private static final int PERMISSION_REQUEST = 1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
checkPermission();
setContentView(R.layout.activity_main);
}
// 检查权限
private void checkPermission() {
mUnPermissionList.clear();
//判断哪些权限未授予
for (int i = 0; i < permissions.length; i++) {
if (ContextCompat.checkSelfPermission(this, permissions[i]) != PackageManager.PERMISSION_GRANTED) {
mUnPermissionList.add(permissions[i]);
}
}
/**
* 判断是否为空
*/
if (mUnPermissionList.isEmpty()) {//未授予的权限为空,表示都授予了
} else {//请求权限方法
String[] permissions = mUnPermissionList.toArray(new String[mUnPermissionList.size()]);//将List转为数组
ActivityCompat.requestPermissions(MainActivity.this, permissions, PERMISSION_REQUEST);
}
}
/**
* 响应授权
* 这里不管用户是否拒绝,都进入首页,不再重复申请权限
*/
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case PERMISSION_REQUEST:
break;
default:
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
break;
}
}
其余部分与内部存储相比, 就只是多了一步, 即最先得创建文件.
// 创建文件夹对象
File dir = new File(Environment.getExternalStorageDirectory(), "axiao");
if (!dir.exists()) { // 如果该文件夹不存在的话就创建目标文件夹
dir.mkdirs();
}
// 创建文件对象
File file = new File(dir, mFILENAME);
if (!file.exists()) { // 如果该文件不存在的话就目标创建文件
file.createNewFile();
}
fileOutputStream = new FileOutputStream(file); // 创建一个通向目标文件的输出流
fileOutputStream.write(content.getBytes());
fileOutputStream.close();
File file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "axiao", mFILENAME);
fileInputStream = new FileInputStream(file);
fileInputStream.read(buff)
fileInputStream.close();
public class FileActivity extends AppCompatActivity {
private EditText mEtInput;
private Button mBtnSave, mBtnShow;
private TextView mTvShow;
private final String mFILENAME = "test.txt";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_file);
mEtInput = findViewById(R.id.et_input);
mBtnSave = findViewById(R.id.btn_save_data);
mBtnShow = findViewById(R.id.btn_show_data);
mTvShow = findViewById(R.id.tv_show_content);
mBtnSave.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
save(mEtInput.getText().toString());
}
});
mBtnShow.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mTvShow.setText(read());
}
});
}
// 存储数据
private void save(String content) {
FileOutputStream fileOutputStream = null;
try {
// 内部存储
// fileOutputStream = openFileOutput(mFILENAME, MODE_PRIVATE);
// 外部存储
// 创建文件夹对象
File dir = new File(Environment.getExternalStorageDirectory(), "axiao");
if (!dir.exists()) { // 如果该文件夹不存在的话就创建目标文件夹
dir.mkdirs();
}
// 创建文件对象
File file = new File(dir, mFILENAME);
if (!file.exists()) { // 如果该文件不存在的话就目标创建文件
file.createNewFile();
}
fileOutputStream = new FileOutputStream(file); // 创建一个通向目标文件的输出流
fileOutputStream.write(content.getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fileOutputStream != null) {
try {
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
一般不常用…要用再说, 自己写代码时出现一个问题, 广播成功发送了, 并且广播也成功注册了, 但是收不到广播, 最后查明原因是: 我发送广播的时刻, 接收广播的Activity的 onDestroy() 方法已经调用了, 也就是已经销毁了, 故我再进去的时候还是没有之前的数据, 即没有接收到广播. 于是吸取教训: 运用广播的时候, 一般是之前的 Activity 发送广播, 而在它之后的 Activity 来接受广播, 这样才能保证发送广播的时刻, 接收广播的 Activity 也存在.
广播的使用很简单, 就分为四个部分:
LocalBroadcastManager.getInstance(context).sendBroadcast(new Intent("com.test.action"));
LocalBroadcastManager.getInstance(context).registerReceiver(deviceEventReceiver, intentFilter);
LocalBroadcastManager.getInstance(context).unregisterReceiver(deviceEventReceiver)
以下代码参考自 火龙映天
Intent intent = new Intent("com.test.action");
LocalBroadcastManager.getInstance(context).sendBroadcast(intent);
接收广播:
//注册广播
private void registerDeviceEventReceiver() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.test.action");
LocalBroadcastManager.getInstance(context).registerReceiver(deviceEventReceiver, intentFilter);
}
//注销广播
private void unregisterDeviceEventReceiver() {
LocalBroadcastManager.getInstance(context).unregisterReceiver(deviceEventReceiver);
}
//广播接收器
private BroadcastReceiver deviceEventReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction() == null) {
return;
}
if ("com.test.action".equals(intent.getAction())) {
//TODO: 逻辑处理
}
}
};