Anykey图像优化及文字头像生成与加载。
(左图是修改前的,右图是修改后的)
这里我们做了以下调整:
头像从CircleImageView换成了CircleTextImageView 头像增加了外描边 右侧的按钮图标全部重制了一遍 文字和编辑框的大小和间距也进行了调整(事实上代码更简洁了,后面会贴出全部的代码)在正式开始之前,我们先来解决上章(06章)遗留的问题。
虽然上章的成果在PC模拟器上能够正常运行,但是使用真机调试只要一进入就会OOM(Out of Memory)崩溃。
另外,在上章5.2章节提到的方法是可以临时解决此崩溃的:
在
AndroidManifest.xml中添加一行代码:
不过此方法治标不治本。因为内存溢出的真正原因在于图片过大且处理不正确(仅指本次案例)。
所以我们要来优化图片加载过程:
2.1. 先来分析为什么之前的图片加载会导致内存溢出
回想一下我们在哪些地方用到了加载图片:
右分页头像 右分页所有的按钮图标
把头像删除,会发现仍然崩溃,说明是第二条造成的。
崩溃的原因在于图标的分辨率过高,由于这些图片都是我在素材网上随便搜的,所以分辨率参差不齐(其中第一个按钮明显都有些模糊了),但多数都是几百的分辨率,所以图标看起来不大,但吃起内存相当惊人。
了解了问题所在,下面我们就把这些图标重制一遍:
2.2. 图标素材重制
使用“Iconfont(阿里巴巴矢量图)”+“Android Asset Studio”自制全套高清图标
1. 首先打开阿里巴巴矢量图标库网站:http://www.iconfont.cn/
比如我搜索“email”,选择其中一个,下载它的SVG或者PNG格式的文件:
2. 然后打开Android Asset Studio网站:https://romannurik.github.io/AndroidAssetStudio/index.html
点击第六个Generic icon generator(第二个是生成启动图标的,也可以用):
点击Image,在文件夹中选择你下载好的email.svg或者email.png,在Name处改成“ic_email”,就能自动生成所有屏幕密度等级的图标了(以压缩包的形式下载到本地)。
按照此方法找全所有的按钮图标(分组、排序、锁、三点……):
比如我找到的图标素材打包放在了度盘(2017年12月9日):链接: https://pan.baidu.com/s/1dFPdBkH 密码: dgwu
有了这些图标后,还要在Android Studio中建立相应的目录:
3. 在Android Studio中新建分辨率目录(切换到Project视图)
右键 res目录 - New - Android resource directory - 设置第二行为drawable,在下面的列表框中找到“Density”传到右边的框,就可以新建drawable相关的资源目录了(如图所示)。
一共有6个目录,我们最常用到的有两个(加粗显示):
drawable-ldpi (低分辨率目录,基本用不上了) drawable-mdpi (其实这个就是默认的drawable目录了) drawable-hdpi (2014主流机型) drawable-xhdpi (2015-2016主流机型) drawable-xxhdpi (2016-2017旗舰机型) drawable-xxxhdpi (三星S8、S8+)
由此可见,在上章中我们往drawable中存放高分辨率的图片且完全不压缩地加载是多么危险的一件事。
图标放置完毕后,这里姑且还是把右分页的布局再写一遍(其中涉及到了一些布局的细微调整,另外按钮的id名字全部都改了,自己注意着一一对照手动修改,按shift+f6可全局变更)
4.
page_card_new.xml
至此,我们完整地完成了图标的重制工作,本章的上半章到此结束。
重新运行一下app,你会发现它在真机上也能非常流畅地打开了,再也没有原先滑动时的卡顿感。
下面开始本章的下半章内容——文字头像的生成与加载。
3. 文字头像的生成、保存与加载
3.1. 从用户的使用情景分析头像的来源
|
|
|
---|---|---|
|
|
|
|
|
|
|
|
|
本章只制作第二个,其他都不做。也就是说本章只考虑如何根据标题文字来生成相应的“文字头像”,其他头像的生成方式会放在以后再讲。
3.2. 文字头像的整体实现思路
(注:“开始在画布上写字”是以前的想法,下面讲的是用CircleTextImageView的set属性来设置,比这个要简单一些。)
从上面这张图来看,我们生成的文字头像必须具有以下属性:
文字内容(1字到多字均可) 文字大小(可不做) 文字颜色 背景填充色
下面开始正式的制作过程:
3.3. 使用CircleTextImageView取代CircleImageView
1. 添加依赖库(把Android Studio的视图切换回Android目录视图):
app:build.gradle
dependencies {
...
compile 'com.github.thinkcool:circletextimageview:1.0.20151218'
// 另外此行可以删除了:
// compile 'de.hdodenhof:circleimageview:2.2.0'
}
2. 替换之前的代码:
2.1.
MainActivity.java(这里看懂就好,待会儿会贴出整体的代码)
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//以下变量是与右分页相关的控件
...
private CircleTextImageView userHead;
@Override
protected void onCreate(Bundle savedInstanceState) {
...
//用LayoutInflater来绑定布局
LayoutInflater inflater = getLayoutInflater();
page1 = inflater.inflate(R.layout.page_card_list, null); //预加载左分页
page2 = inflater.inflate(R.layout.page_card_new, null); //预加载右分页
...
//绑定分页的按钮
...
userHead = (CircleTextImageView) page2.findViewById(R.id.user_head);
}
...
}
2.2.
page_card_new.xml
...
...
2.3.
SaveUserInfo.java
package com.likianta.anykey;
import android.content.Context;
import android.content.SharedPreferences;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import com.google.gson.Gson;
import de.hdodenhof.circleimageview.CircleImageView;
/**
* Created by Likianta_DoDoRa on 2017/11/27 0027.
*/
public class SaveUserInfo extends MainActivity {
private EditText userTitle;
private EditText userName;
private EditText userPassword;
private EditText userUrl;
private EditText userNote;
private CircleTextImageView userHead;
private String uTitle;
private String uName;
private String uPassword;
private String uUrl;
private String uNote;
private Card card;
public SaveUserInfo(View pageview) {
LogUtil.d("向SaveUserInfo()传入右分页布局");
userTitle = (EditText) pageview.findViewById(R.id.userTitle);
userName = (EditText) pageview.findViewById(R.id.userName);
userPassword = (EditText) pageview.findViewById(R.id.userPassword);
userUrl = (EditText) pageview.findViewById(R.id.userUrl);
userNote = (EditText) pageview.findViewById(R.id.userNote);
userHead = (CircleTextImageView) pageview.findViewById(R.id.userHead);
uTitle = userTitle.getText().toString();
uName = userName.getText().toString();
uPassword = userPassword.getText().toString();
uUrl = userUrl.getText().toString();
uNote = userNote.getText().toString();
LogUtil.d("User note is " + uNote);
getNewCard(); //生成新卡片的操作
}
//检测保存时的标题栏的文字是否为空,空的话禁止保存并提醒填写
public boolean isTitleEmpty() {
if (uTitle.equals("")) {
userTitle.setHint("请输入标题");
userTitle.setFocusable(true);
userTitle.setFocusableInTouchMode(true);
userTitle.requestFocus();
//通过调用输入管理器来弹出软键盘
InputMethodManager inputMethodManager = (InputMethodManager) userTitle.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(userTitle, 0);
return true;
}
return false;
}
public Card getNewCard() {
String uSummary = uName + "\n" + uPassword + "\n" + uNote; //卡片的摘要=用户名+密码+备注
card = new Card(uTitle, uSummary, uHead, resId); //注意增加了两个属性:头像和resid
return card;
}
}
2.4.
card.xml
至此,CircleTextImageView取代CircleImageView工作完成,下面开始实现头像的生成过程:
3.4. 生成右分页的头像
先制作右分页的头像生成,然后在3.5章再制作按下保存按钮如何在左分页出现。
3.4.1. 制作右分页的头像生成GenerateUserHead
新建
GenerateUserHead.java:
package com.likianta.anykey;
import android.support.annotation.DrawableRes;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import com.thinkcool.circletextimageview.CircleTextImageView;
/**
* Created by Likianta_DoDoRa on 2017/12/5 0005.
*/
public class GenerateUserHead extends AppCompatActivity {
// 传入标题内容及头像view
public GenerateUserHead(String content, CircleTextImageView userHead) {
if (!content.equals("")) { // 如果内容不为空,则执行以下方法(为空则什么都不做)
// 判断标题字符串的长度(字数)
switch (content.length()) {
case 1:
// 如果只有一个字,则无需检查例外规则,直接开始生成
userHead.setImageDrawable(null); // 1. 设置头像为null
userHead.setText(content); // 2. 载入文本显示
userHead.setTextSize(120); // 3. 设置文字大小
userHead.setTextColor(0xffffffff); // 4. 设置文字颜色
userHead.setFillColor(0xff000000); // 5. 设置背景填充色
break;
case 2:
// 检查2字例外规则
// 本章不考虑,以后会补充上
// 不符合例外,则执行以下方法
userHead.setImageDrawable(null);
userHead.setText(content);
userHead.setTextSize(120);
userHead.setTextColor(0xffffffff);
userHead.setFillColor(0xff000000);
break;
default:
// 检查多字例外规则
// 本章不考虑,以后会补充上
// 不符合例外,则执行以下方法
userHead.setImageDrawable(null);
userHead.setText(content.substring(0, 2));
userHead.setTextSize(120);
userHead.setTextColor(0xffffffff);
userHead.setFillColor(0xff000000);
break;
}
}
}
/**
* Exceptional rules
* if user input a title content equals to the built-in list, generate a special avatar instead of default text avatar
*/
}
在上面的代码中,我们设置了两个例外规则,为什么2字和多字的判断情况要分开呢?
因为2字的情况比较少,比如“QQ”、“B站”、“淘宝”等图标,除了这几个常用的基本上就没有什么需要判断的了;而多字的情况就比较复杂,特别是颜文字的处理,为了让它们能够较好的显示,可能需要更多操作,所以为了检查效率,二者将做分开处理。
另外例外规则方法将会返回一个resId值,我们就是依靠检查resId是否为0来识别card到底用的是文字头像还是图片头像的。
试着去运行一下,我们会发现输入文字后头像根本就没有改变。这是为什么呢?
因为我们忘了最基本的工作——在MainActivity中设置文本监听器:
3.4.2. 设置监听器
为了让头像能够在输入标题内容后及时地生成,我们至少需要设置4个监听器,一旦监听器被触发,就使用
GenerateUserHead方法生成头像:
userTitle编辑框设置keyListener,监听用户按下Enter键的操作 有些用户不喜欢按Enter键换到下一个编辑框,所以会无法触发第一个。因此需要给下面的userName编辑框设置FocusListener 有些用户可能不需要设置帐号,也有不需要设置密码的情况;但作为常识,一个密码至少会有帐号或者密码其中之一被填写,所以给userPassword也设置一个FocusListener 当然,如果上述情况真的发生了(帐号、密码都是空的),我们也要做一个保底的方法,那就是给userSaveButton设置一个ClickListener。尽管这个已经做不到“及时地生成头像”,但它能保证头像肯定会生成
所以我们要在MainActivity中完成这四个监听工作:
MainActivity.java(整体代码如下)
package com.likianta.anykey;
import android.content.Context;
import android.content.Intent;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.thinkcool.circletextimageview.CircleTextImageView;
import java.util.ArrayList;
import java.util.List;
import me.relex.circleindicator.CircleIndicator;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
//以下变量是与分页相关的变量
private TextView titleAll; //标题文字“全部”
private TextView titleNew; //标题文字“新增”
private CircleIndicator indicator; //滚动指示器
private View page1; //左分页
private View page2; //右分页
private ViewPager viewPager; //控制分页逻辑的容器
private ArrayList pageList; //装载分页元素的容器
//以下变量是与左分页相关的控件
private RecyclerView recyclerView; //卡片列表
private CardAdapter cardAdapter;
private List cardList = new ArrayList(); //卡片数据
//以下变量是与右分页相关的控件
private EditText userTitle;
private EditText userName;
private EditText userPassword;
private EditText userUrl;
private EditText userNote;
private CircleTextImageView userHead;
private Button userSaveButton;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/*
* 强制隐藏软键盘
* 由于刚一进入主界面,右分页的编辑框会率先获取焦点并唤起软键盘,对用户体验造成影响,因此在一开始就把软键盘给强制隐藏掉
* http://blog.csdn.net/Vivian8725118/article/details/23184501
*/
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_HIDDEN);
//开始绑定主界面的按钮
viewPager = (ViewPager) findViewById(R.id.viewPager);
titleAll = (TextView) findViewById(R.id.title_all);
titleNew = (TextView) findViewById(R.id.title_new);
indicator = (CircleIndicator) findViewById(R.id.indicator);
//用LayoutInflater来绑定布局
LayoutInflater inflater = getLayoutInflater();
page1 = inflater.inflate(R.layout.page_card_list, null); //预加载左分页
page2 = inflater.inflate(R.layout.page_card_new, null); //预加载右分页
pageList = new ArrayList(); //pageList被实例化为装载View元素的数组
pageList.add(page1);
pageList.add(page2);
//add的先后顺序不要搞错,先add的就是array[0]位置的元素了
//绑定分页的按钮
recyclerView = (RecyclerView) page1.findViewById(R.id.recyclerView);
userTitle = (EditText) page2.findViewById(R.id.user_title);
userName = (EditText) page2.findViewById(R.id.user_name);
userPassword = (EditText) page2.findViewById(R.id.user_password);
userUrl = (EditText) page2.findViewById(R.id.user_url);
userNote = (EditText) page2.findViewById(R.id.user_note);
userHead = (CircleTextImageView) page2.findViewById(R.id.user_head);
//绑定小按钮
ImageView userTitleButton = (ImageView) page2.findViewById(R.id.user_title_button);
ImageView userNameButton = (ImageView) page2.findViewById(R.id.user_name_button);
ImageView userPasswordButton = (ImageView) page2.findViewById(R.id.user_password_button);
ImageView userBoundButton = (ImageView) page2.findViewById(R.id.user_bound_button);
ImageView userUrlButton = (ImageView) page2.findViewById(R.id.user_url_button);
ImageView userNoteButton = (ImageView) page2.findViewById(R.id.user_note_button);
userSaveButton = (Button) page2.findViewById(R.id.user_save_button);
//初始化左分页
initPager1();
//初始化右分页(在onCreate方法中没必要做。在点击保存按钮后使用该方法来清空右分页的表单编辑框)
//initPager2();
//监听按钮点击
//顶部的指示器文字被点击
titleAll.setOnClickListener(this);
titleNew.setOnClickListener(this);
//右分页的小按钮被点击(相关事件还没有做)
userTitleButton.setOnClickListener(this);
userNameButton.setOnClickListener(this);
userPasswordButton.setOnClickListener(this);
userBoundButton.setOnClickListener(this);
userUrlButton.setOnClickListener(this);
userNoteButton.setOnClickListener(this);
userSaveButton.setOnClickListener(this);
//帐号编辑框监听器(触发生成右分页头像)
userName.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
new GenerateUserHead(userTitle.getText().toString(), userHead);
}
}
});
//密码编辑框监听器(触发生成右分页头像)
userPassword.setOnFocusChangeListener(new View.OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
new GenerateUserHead(userTitle.getText().toString(), userHead);
}
}
});
//test...
Button test = (Button) findViewById(R.id.test);
test.setOnClickListener(this);
}
//初始化左分页
public void initPager1() {
PagerAdapter pagerAdapter = new PagerAdapter() {
//https://www.cnblogs.com/weixing/archive/2013/10/11/3363951.html
//获取页卡总数量
@Override
public int getCount() {
return pageList.size();
}
//判断是否由对象生成界面,这个很重要,是用来把pageView数组中的page1和page2生成到当前布局中的方法
@Override
public boolean isViewFromObject(View view, Object object) {
return view == object;
}
//使从ViewGroup中移出当前View
@Override
public void destroyItem(ViewGroup arg0, int arg1, Object arg2) {
((ViewPager) arg0).removeView(pageList.get(arg1));
}
//返回一个对象,这个对象表明了PagerAdapter适配器选择哪个对象放在当前的ViewPager中
@Override
public Object instantiateItem(ViewGroup arg0, int arg1) {
//这个方法用来实例化页卡
((ViewPager) arg0).addView(pageList.get(arg1));
return pageList.get(arg1);
}
};
viewPager.setAdapter(pagerAdapter); //绑定适配器
indicator.setViewPager(viewPager); //装载indicator
//设置viewPager的初始界面为第一个界面
viewPager.setCurrentItem(0); //这里的0对应的是viewPager[0],也就是page1了
//添加切换界面的监听器
viewPager.addOnPageChangeListener(new MyOnPageChangeListener());
//为左分页加载卡片列表
PageRender();
}
//初始化右分页
public void initPager2() {
userTitle.setText("");
userName.setText("");
userPassword.setText("");
userUrl.setText("");
userNote.setText("");
// 本来想在这里设置头像初始化为默认头像的,但不知道什么原因,如果在此处重置头像为默认,会发现左分页的卡片头像也会“突变”为默认
// 而把重置头像的业务放到页面监听里面就不会引起此bug,所以不得已把头像重置的代码放到MyOnPageChangeListener的case1里面了
}
//渲染分页
public void PageRender() {
cardList = new SavedToMySharedPrefs(MainActivity.this, "card_data").getCardData();
cardAdapter = new CardAdapter(cardList); //将数组数据适配为卡片数据
recyclerView.setLayoutManager(new LinearLayoutManager(this)); //为recyclerView设置线性布局,使内部元素呈线性排列
recyclerView.setAdapter(cardAdapter); //开始加载卡片
}
@Override
public void onClick(View view) {
// 监听事件合集
switch (view.getId()) {
case R.id.title_all:
viewPager.setCurrentItem(0);
break;
case R.id.title_new:
viewPager.setCurrentItem(1);
break;
case R.id.user_save_button:
//点击右分页的保存按钮
LogUtil.d("ma You clicked user_save_button button.");
//首先判断标题是不是空的,空的话必须填写标题,其他字段则允许为空
if (new SaveUserInfo(MainActivity.this).isTitleEmpty(userTitle)) {
Toast.makeText(MainActivity.this, "标题不能为空!", Toast.LENGTH_SHORT).show();
break;
}
//保底操作,确保生成了右分页头像
new GenerateUserHead(userTitle.getText().toString(), userHead);
Card card = new SaveUserInfo(page2).getNewCard();
cardList.add(0, card); //在零号位(也就是第一位)添加这张新卡片
cardAdapter.notifyItemInserted(0); //添加后要给RecyclerView释放一个更新信号。具体方法写在CardAdapter.java里
viewPager.setCurrentItem(0); //自动跳转到左分页
recyclerView.scrollToPosition(0); //自动跳转到列表首部,方便用户看到自己新增的卡片
break;
//test...
case R.id.test:
new SavedToMySharedPrefs(MainActivity.this,"card_data").setCardData(cardList);
MainActivity.this.finish();
break;
}
}
@Override
public void onDestroy() {
super.onDestroy();
// Save all data.
new SavedToMySharedPrefs(MainActivity.this, "card_data").setCardData(cardDataList);
}
//页面滚动监听器功能,实现标签页左右滑动切换效果
public class MyOnPageChangeListener implements ViewPager.OnPageChangeListener {
@Override
public void onPageSelected(int index) {
switch (index) {
case 0:
titleAll.setTextColor(0xff000000);//0x表示整型,ff表示透明度为0,最后的6位数字表示颜色,必须这样写,不能省略
titleNew.setTextColor(0xff8e8e8e);
InputMethodManager inputMethodManager = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(MainActivity.this.getCurrentFocus().getWindowToken(), 0); //强制隐藏软键盘
break;
case 1:
titleNew.setTextColor(0xff000000);
titleAll.setTextColor(0xff8e8e8e);
userHead.setText("");
userHead.setImageResource(R.drawable.avatar_test);
break;
}
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
}
}
PS:这里我没有写userTitle的按键监听器,因为它会造成无法自动跳转到下一个编辑框的问题。
注(2017年12月11日):该问题已查明原因,具体可以看章末的更新修复章节4.1小结。
3.5. 使头像在左分页相应卡片中出现
要想使左分页新卡片的加载,我们需要改动Card和CardAdapter,前者负责提供头像接口,后者负责头像数据的接入。
3.5.1. 完善Card属性
还记得前几章我们一直在用的卡片吗?这个典型的java bean里面只包含了两个属性——标题(String)和摘要(String)。
本章我们要给它增加一个头像属性(circleTextImageView)。
Card.java
package com.likianta.anykey;
import com.thinkcool.circletextimageview.CircleTextImageView;
/**
* Created by Likianta_DoDoRa on 2017/11/21 0021.
*/
public class Card {
private String cardTitle; // 卡片标题
private String cardSummary; // 卡片的摘要内容(摘要=用户名+密码+备注)
// 头像相关
// 本章(07章)只制作文字头像,不制作资源头像
private String cardHeadText;
private int cardHeadTextColor;
private int cardHeadFillColor;
private int cardHeadResId = 0; // 因为用不到资源图片,所以默认值设为0
public Card(String cardTitle, String cardSummary, CircleTextImageView cardHead, int resId) {
// 将外界传入的参数赋值给Card内部类
this.cardTitle = cardTitle;
this.cardSummary = cardSummary;
if (resId == 0) { // 判断资源id,如果是默认值,那么就按“文字头像”属性处理
cardHeadText = cardHead.getTextString();
cardHeadTextColor = cardHead.getTextColor();
cardHeadFillColor = cardHead.getFillColor();
} else { // 如果资源id不为0,,则表示它是资源图片;不过本章只是先写全这个判断而已,后面并不会用到这个else情况
cardHeadResId = resId;
}
}
public int getCardHeadResId() {
return cardHeadResId;
}
public void setCardHeadResId(int cardHeadResId) {
this.cardHeadResId = cardHeadResId;
}
public String getCardHeadText() {
return cardHeadText;
}
public void setCardHeadText(String cardHeadText) {
this.cardHeadText = cardHeadText;
}
public int getCardHeadTextColor() {
return cardHeadTextColor;
}
public void setCardHeadTextColor(int cardHeadTextColor) {
this.cardHeadTextColor = cardHeadTextColor;
}
public int getCardHeadFillColor() {
return cardHeadFillColor;
}
public void setCardHeadFillColor(int cardHeadFillColor) {
this.cardHeadFillColor = cardHeadFillColor;
}
public String getCardSummary() {
return cardSummary;
}
public void setCardSummary(String cardSummary) {
this.cardSummary = cardSummary;
}
public String getCardTitle() {
return cardTitle;
}
public void setCardTitle(String cardTitle) {
this.cardTitle = cardTitle;
}
}
3.5.2. CardAdapter
CardAdapter着重修改了onBind的内容,另外
static class CardViewHolder也有轻微的改动,其他基本上没有变动。整体代码如下:
CardAdapter.java
package com.likianta.anykey;
import android.content.Context;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import com.thinkcool.circletextimageview.CircleTextImageView;
import java.util.List;
/**
* Created by Likianta_DoDoRa on 2017/11/21 0021.
*/
public class CardAdapter extends RecyclerView.Adapter {
private List cardList;
private Context context; // 定义一个context,由于没有赋值,所以现在是null的状态
public CardAdapter(List cardList) {
this.cardList = cardList;
}
@Override
public CardViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// 首先传入context
if (context == null) {
context = parent.getContext(); // 这里的parent就是指列表页(RecyclerView)所属的类了
}
View card = LayoutInflater.from(context).inflate(R.layout.card, parent, false);
// inflate的第三个参数表示是否连接该布局和父控件。由于系统已经插入了这个布局到父控件,所以设为false(若写true则会产生一个多余的parent)
return new CardViewHolder(card); // 获得了一张白卡
}
// 开始给白卡绑数据
@Override
public void onBindViewHolder(CardViewHolder whiteCard, int position) {
Card card = cardList.get(position); // 通过位置参数判断用户点的是哪个卡片
// 设置标题文字和摘要文字,比较好理解
whiteCard.cardTitle.setText(card.getCardTitle());
whiteCard.cardSummary.setText(card.getCardSummary());
// 关键在于这里,判断resId的值
if (card.getCardHeadResId() == 0) {
whiteCard.cardHead.setText(card.getCardHeadText());
whiteCard.cardHead.setTextSize(60);
whiteCard.cardHead.setTextColor(card.getCardHeadTextColor());
whiteCard.cardHead.setFillColor(card.getCardHeadFillColor());
} else {
whiteCard.cardHead.setImageResource(card.getCardHeadResId());
// 当然这里也可以采用Glide来加载
}
}
//获取列表中卡片总数量
@Override
public int getItemCount() {
return cardList.size();
}
public void add(int position, Card card) {
cardList.add(position, card);
notifyItemInserted(position);
}
public void remove(Context context, int position) {
if (position < 0) {
Toast.makeText(context, "列表中并没有结果!", Toast.LENGTH_SHORT).show();
} else {
cardList.remove(position);
notifyItemRemoved(position);
}
}
static class CardViewHolder extends RecyclerView.ViewHolder {
CardView cardView;
TextView cardTitle;
TextView cardSummary;
CircleTextImageView cardHead;
public CardViewHolder(View view) {
//引入外界的item,在本类中开始实例化
super(view);
cardView = (CardView) view;
cardTitle = (TextView) view.findViewById(R.id.cardTitle);
cardSummary = (TextView) view.findViewById(R.id.cardSummary);
cardHead = (CircleTextImageView) view.findViewById(R.id.cardHead);
}
}
}
至此代码的编写工作已经全部完成。
整理一下思路,我们点击保存按钮,
MainActivity的这个地方开始工作:
里面调用了
SaveUserInfo类,并get到相应的card,然后加载并通知RecyclerView更新,左分页的活动就圆满完成了。
另外,此时再左滑回到右分页,会发现右分页的头像被重置为了默认的头像,这是因为MainActivity中的ViewPager起了作用:
本章内容到此结束。下章将会实现Anykey的分组功能。
4. 更新修复
下面是对上一章节代码细节的修复和改善:
4.1. 解决在登录界面按Enter键登录事件被连续触发两次的bug
这是因为键盘动作的监听条件没有判断好造成的。
特别值得注意的是,手指在键盘上的一个动作是被当成ACTION_DOWN和
ACTION_UP两个事件考虑的。
如果ACTION_DOWN事件发生在某个View的范围之内,则后续的ACTION_MOVE,ACTION_UP和ACTION_CANCEL等事件都将被发往该View,即使事件已经出界了。
也就是说之前我们判断按下Enter会
callOnClick(),如果不指明
ACTION_DOWN或
ACTION_UP,则会被连续执行两次!
因此修改
LoginActivity如下:
public class LoginActivity extends AppCompatActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
...
loginPassword.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if (i == keyEvent.KEYCODE_ENTER && keyEvent.getAction() == KeyEvent.ACTION_UP) {
loginOn.callOnClick();
return true;
}
return false;
/* 关于返回值的说明
* 返回值为true,表示事件已完全处理,系统无需再处理此键
* 返回值为false,表示事件处理过后,还要交给系统继续处理
* 参考此回答:https://zhidao.baidu.com/question/1430105248859125459.html
*/
}
});
}
}