RecylerView(GridView布局)实现充值界面效果
RecylerView(GridView布局)实现充值界面效果
项目中有一个充值的效果如下:
效果图
之前为了赶项目进度,也没有多想,这不是很简单的么?5个TextView加1个EditText不就搞定了么?
的确这样做是可以实现的,而且也在规定时间内完成了项目。(但是存在局限性)
由于项目中多次的使用了RecycleView,然后发现这个用RecycleView实现效果会更好(布局更灵活,不限制个数),完成后的效果:
有角标
无角标
-
1、数据传递效果的展示
首先分析下这个界面需要的数据,由于这个界面是后台通过两个字段判断实现的效果
String data = "3,9,30,50,100";
//isDiscount :1、有角标 2、无角标
String isDiscount = "1";
我们首先要通过IsDiscount这个字段来判断是否显示带角标的控件,以及data字段来判断动态添加几个控件
-
2、界面实现效果分析
1、组件选择
文章开始就说到了,我们选用的是RecycleView这个控件结合GridView布局实现效果
2、RecyeView Adapter布局分析
①有角标TextView
②正常无角标TextView
③EditText输入框
布局如上三种,大家都知道RecycleView的强大,现在我们就利用它其中的ItemType这个功能,RecyleView很多复杂的界面都是通过ItemType这个属性来实现的。
3、具体代码的实现
①ItemType种类的控制
@Override
public DemoAdapter.BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case ItemModel.ONE:
///左上角三角标识布局;
return new CornorViewHoler(LayoutInflater.from(parent.getContext()).inflate(R.layout.corner_one, parent, false));
case ItemModel.TWO:
return new OneViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.one, parent, false));
case ItemModel.THREE:
return new TWoViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.two, parent, false));
}
return null;
}
在onCreateViewHolde这个方法中添加所需要的三种布局文件
②Item数据封装
在这个Adapter中,由于我需要知道几个条件,第一个是当前选中的状态是否是免费的标志,第二个是当前选中的值,所以封装了一个ItemModel的类用于处理数据
public class ItemModel implements Serializable {
//左上角三角图标
public static final int ONE = 1001;
//textview布局
public static final int TWO = 1002;
//edittext布局
public static final int THREE = 1003;
public int type;
public Object data;
//是否免费的标志
public boolean isFree;
public ItemModel(int type, Object data, boolean isFree) {
this.type = type;
this.data = data;
}
}
好了前期的准备工作差不多了,现在我们就该对Adapter中的逻辑进行处理了。先上Adapter的完整代码
public class DemoAdapter extends RecyclerView.Adapter<DemoAdapter.BaseViewHolder> {
private ArrayList<ItemModel> dataList = new ArrayList<>();
private int lastPressIndex = -1;
private Context mContext;
public void replaceAll(ArrayList<ItemModel> list, Context context) {
mContext = context;
dataList.clear();
if (list != null && list.size() > 0) {
dataList.addAll(list);
}
notifyDataSetChanged();
}
@Override
public DemoAdapter.BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case ItemModel.ONE:
///左上角三角标识布局;
return new CornorViewHoler(LayoutInflater.from(parent.getContext()).inflate(R.layout.corner_one, parent, false));
case ItemModel.TWO:
return new OneViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.one, parent, false));
case ItemModel.THREE:
return new TWoViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.two, parent, false));
}
return null;
}
@Override
public void onBindViewHolder(DemoAdapter.BaseViewHolder holder, int position) {
holder.setData(dataList.get(position).data);
}
@Override
public int getItemViewType(int position) {
return dataList.get(position).type;
}
@Override
public int getItemCount() {
return dataList != null ? dataList.size() : 0;
}
public class BaseViewHolder extends RecyclerView.ViewHolder {
public BaseViewHolder(View itemView) {
super(itemView);
}
void setData(Object data) {
}
}
private class OneViewHolder extends BaseViewHolder {
private TextView tv;
public OneViewHolder(View view) {
super(view);
tv = (TextView) view.findViewById(R.id.tv);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG", "OneViewHolder: ");
notyfy = 0;
int position = getAdapterPosition();
ItemModel model = dataList.get(position);
Log.e("TAG", "OneViewHolder: " + model.toString());
EventBus.getDefault().post(model);
if (lastPressIndex == position) {
lastPressIndex = -1;
} else {
lastPressIndex = position;
}
notifyDataSetChanged();
}
});
}
@Override
void setData(Object data) {
if (data != null) {
String text = (String) data;
tv.setText(text);
if (getAdapterPosition() == lastPressIndex) {
tv.setSelected(true);
tv.setTextColor(ContextCompat.getColor(itemView.getContext(), R.color.white));
} else {
tv.setSelected(false);
tv.setTextColor(ContextCompat.getColor(itemView.getContext(), R.color.blue_500));
}
}
}
}
int notyfy = 0;
private class TWoViewHolder extends BaseViewHolder {
private EditText et;
private String chargeFunds;
public TWoViewHolder(View view) {
super(view);
et = (EditText) view.findViewById(R.id.et);
final int position = getAdapterPosition();
//ItemModel model = dataList.get(position);
//Log.e("TWoViewHolder", "TWoViewHolder: "+model.toString());
Log.e("TWoViewHolder", "TWoViewHolder: ");
et.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
if (lastPressIndex != position) {
notifyItemChanged(lastPressIndex);
lastPressIndex = position;
}
}
}
});
et.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (charSequence != null || !charSequence.equals("")) {
try {
int str = Integer.parseInt(String.valueOf(charSequence));
if (str < 9) {
Toast.makeText(mContext, "输入的金额小于最小支付金额9元", Toast.LENGTH_SHORT).show();
et.setSelection(et.getText().length());
}
if (str > 2000) {
Toast.makeText(mContext, "输入的金额大于最大支付金额2000元", Toast.LENGTH_SHORT).show();
et.setText("2000");
et.setSelection(et.getText().length());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void afterTextChanged(Editable editable) {
if (editable.length() > 0) {
String inputText = et.getText().toString().trim();
if (Double.parseDouble(inputText) < 9) {
chargeFunds = "9";
} else if (Double.parseDouble(inputText) > 2000) {
chargeFunds = "2000";
} else {
chargeFunds = inputText;
}
}
String funds = chargeFunds + "元";
ItemModel model = new ItemModel(ItemModel.THREE, funds, false);
EventBus.getDefault().post(model);
}
});
}
@Override
void setData(Object data) {
super.setData(data);
final int position = getAdapterPosition();
if (position == lastPressIndex)
et.requestFocus();
else
et.clearFocus();
}
}
private class CornorViewHoler extends BaseViewHolder {
private TextView tv;
public CornorViewHoler(View view) {
super(view);
tv = (TextView) view.findViewById(R.id.tv);
//int position = getAdapterPosition();
//ItemModel model = dataList.get(position);
//Log.e("TAG", "OneViewHolder: "+model.toString());
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.e("TAG", "OneViewHolder: ");
notyfy = 0;
int position = getAdapterPosition();
ItemModel model = dataList.get(position);
EventBus.getDefault().post(model);
if (lastPressIndex == position) {
lastPressIndex = -1;
} else {
lastPressIndex = position;
}
notifyDataSetChanged();
}
});
}
@Override
void setData(Object data) {
if (data != null) {
String text = (String) data;
tv.setText(text);
if (getAdapterPosition() == lastPressIndex) {
tv.setSelected(true);
tv.setTextColor(ContextCompat.getColor(itemView.getContext(), R.color.white));
} else {
tv.setSelected(false);
tv.setTextColor(ContextCompat.getColor(itemView.getContext(), R.color.blue_500));
}
}
}
}
}
上述代码中数据传递使用的是EventBus3.0,所以需要在gradle文件添加如下引用:
compile 'org.greenrobot:eventbus:3.0.0'
现在开始对Adapter类进行分析,在Adapter类中我们创建了3个ViewHolder(CornorViewHoler、TWoViewHolder、OneViewHolder)在这三个ViewHolder中实现各自的逻辑,同时三个类中同时继承了void setData(Object data)
接口处理是否为选中状态、是否需要更改背景颜色以及状态
其他部分的代码都比较简单,不过在处理TWoViewHolder这个类的时候有点小麻烦(EditText),首先大家看到EditText有一个取值范围的(9~2000),这个通过addTextChangedListener监听来实现即可
et.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
if (charSequence != null || !charSequence.equals("")) {
try {
int str = Integer.parseInt(String.valueOf(charSequence));
if (str < 9) {
Toast.makeText(mContext, "输入的金额小于最小支付金额9元", Toast.LENGTH_SHORT).show();
et.setSelection(et.getText().length());
}
if (str > 2000) {
Toast.makeText(mContext, "输入的金额大于最大支付金额2000元", Toast.LENGTH_SHORT).show();
et.setText("2000");
et.setSelection(et.getText().length());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
@Override
public void afterTextChanged(Editable editable) {
if (editable.length() > 0) {
String inputText = et.getText().toString().trim();
if (Double.parseDouble(inputText) < 9) {
chargeFunds = "9";
} else if (Double.parseDouble(inputText) > 2000) {
chargeFunds = "2000";
} else {
chargeFunds = inputText;
}
}
String funds = chargeFunds + "元";
ItemModel model = new ItemModel(ItemModel.THREE, funds, false);
EventBus.getDefault().post(model);
}
});
到这里差不多写完了,是不是就大功告成了?别急,还有一个问题,点击EditText的时候,TextView的状态还没有消失呢,怎么办?
当页面刷新的时候 如果当前选中的是这个输入框就让它获取到并得到焦点,所以我们在TWoViewHolder类中的setData()
方法中让它获取焦点,将最后的位置传递给它
@Override
void setData(Object data) {
super.setData(data);
final int position = getAdapterPosition();
if (position == lastPressIndex)
et.requestFocus();
else
et.clearFocus();
}
这样就差不多了,然后我们对EdiText的焦点进行监听(setOnFocusChangeListener),Notifycation布局文件即可
et.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
if (lastPressIndex != position) {
notifyItemChanged(lastPressIndex);
lastPressIndex = position;
}
}
}
});
代码讲到这里就可以实现上面的效果了,下面将其他布局文件代码贴出来
MainActivity.class
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private DemoAdapter adapter;
private TextView tvPay;
private TextView tv_recharge_money;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tv_recharge_money = (TextView) findViewById(R.id.tv_recharge_money);
recyclerView = (RecyclerView) findViewById(R.id.recylerview);
tvPay = (TextView) findViewById(R.id.tvPay);
//setHasFixedSize 的作用就是确保尺寸是通过用户输入从而确保RecyclerView的尺寸是一个常数。
// RecyclerView 的Item宽或者高不会变(提升性能)。
recyclerView.setHasFixedSize(true);
//recycleview设置布局方式,GridView (一行三列)
recyclerView.setLayoutManager(new GridLayoutManager(this, 3));
recyclerView.setAdapter(adapter = new DemoAdapter());
adapter.replaceAll(getData(),this);
EventBus.getDefault().register(this);
}
public ArrayList<ItemModel> getData() {
String data = "3,9,30,50,100";
// isDiscount :1、有角标 2、无角标
String isDiscount = "2";
String dataArr[] = data.split(",");
ArrayList<ItemModel> list = new ArrayList<>();
for (int i = 0; i < dataArr.length; i++) {
String count = dataArr[i] + "元";
if (isDiscount.equals("1") && i == 0) {
list.add(new ItemModel(ItemModel.ONE, count, true));
} else {
list.add(new ItemModel(ItemModel.TWO, count, false));
}
}
list.add(new ItemModel(ItemModel.THREE, null, false));
return list;
}
@Subscribe(threadMode = ThreadMode.MAIN)
public void getAdapterClickInfo(ItemModel model) {
String money = model.data.toString().replace("元", "");
tv_recharge_money.setText(money);
if (model.isFree==true){
Toast.makeText(this,"点击的是带免费标签的",Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(this,"点击的是正常标签的",Toast.LENGTH_SHORT).show();
}
}
@Override
protected void onDestroy() {
super.onDestroy();
EventBus.getDefault().unregister(this);
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="com.example.wangchang.testrecharge.MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="12dp"
android:background="@color/grey_300" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@color/white"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="充值账号:"
android:textColor="@color/black" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="15013678968"
android:textColor="@color/yellow_900" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/grey_300" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:background="@color/white"
android:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="充值金额:"
android:textColor="@color/black" />
<TextView
android:id="@+id/tv_recharge_money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="100"
android:textColor="@color/yellow_900" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="@color/grey_300" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="选择充值金币:" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recylerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="12dp" />
</LinearLayout>
<TextView
android:id="@+id/tvPay"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_alignParentBottom="true"
android:layout_margin="24dp"
android:background="@drawable/tv_bg_pay"
android:gravity="center"
android:text="立即充值"
android:textColor="@color/white" />
</RelativeLayout>
corner_one.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="42dp"
android:gravity="center"
android:layout_margin="8dp"
android:padding="12dp"
android:background="@drawable/tv_bg"
android:textColor="@color/blue_500" />
<ImageView
android:layout_margin="8dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:srcCompat="@drawable/free_click"
android:id="@+id/imageView" />
</FrameLayout>
one.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="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/tv"
android:layout_width="match_parent"
android:layout_height="42dp"
android:gravity="center"
android:layout_margin="8dp"
android:padding="12dp"
android:background="@drawable/tv_bg"
android:textColor="@color/blue_500" />
</LinearLayout>
two.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="wrap_content"
android:orientation="vertical">
<EditText
android:id="@+id/et"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="8dp"
android:background="@drawable/bg_et"
android:gravity="center"
android:hint="9-2000元"
android:inputType="number"
android:padding="12dp"
android:textSize="12sp" />
</LinearLayout>
好了,代码到此为止,效果也差不多了,有兴趣的朋友可以自行尝试下,欢迎大家一起交流,如有更好的建议或者意见可致电lcf_spark@163.com