频道栏目
首页 > 资讯 > 其他 > 正文

Anykey图像优化及文字头像生成与加载

17-12-12        来源:[db:作者]  
收藏   我要投稿

Anykey图像优化及文字头像生成与加载。

1. 先看一下最终的效果图

(左图是修改前的,右图是修改后的)

这里我们做了以下调整:

头像从CircleImageView换成了CircleTextImageView 头像增加了外描边 右侧的按钮图标全部重制了一遍 文字和编辑框的大小和间距也进行了调整(事实上代码更简洁了,后面会贴出全部的代码)

2. 解决真机调试发生OOM崩溃的问题

在正式开始之前,我们先来解决上章(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. 从用户的使用情景分析头像的来源

使用情景 图片来源 处理方法
①用户点击外部相册,选择了一张照片 绝对路径文件 使用Glide加载
②用户输入标题文字,app根据文字匹配生成头像 无图片 使用CircleTextImageView的set属性来设置
③用户输入标题文字,app根据特定文字生成内置头像 内置资源图片@drawable/ 使用CircleTextImageView的setResourceId来设置
或者使用Glide加载

本章只制作第二个,其他都不做。也就是说本章只考虑如何根据标题文字来生成相应的“文字头像”,其他头像的生成方式会放在以后再讲。

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
                 */
            }
        });
    }
}
相关TAG标签
上一篇:jQuery全屏滚动插件fullPage.js实例讲解
下一篇:分答项目_知识点_微擎web页面_require
相关文章
图文推荐

关于我们 | 联系我们 | 广告服务 | 投资合作 | 版权申明 | 在线帮助 | 网站地图 | 作品发布 | Vip技术培训 | 举报中心

版权所有: 红黑联盟--致力于做实用的IT技术学习网站