Android开发——易犯错误记录

   在开发过程中有时很容易在一些小细节的地方产生问题,虽然知识点很简单,但有时候也会犯错误,因此特地总结在一篇文章里记录一下。

问题一:TextUtils.isEmpty(str)与str.isEmpty()的区别

   TextUtils.isEmpty(str) 判断时,判断了两步,先判断 str == null,再判断 str.length == 0。
而str.isEmpty()只判断 str.length == 0。因此str.isEmpty()的效率更高一些,但前提是要保证str 不能是 null。(本人就是在这里犯了迷糊,记录一下,防止再犯同样的错误。)

   下面以三个特殊例子说明一下这两个的区别:

 String str1 = "";    
 String str2 = " ";   
 String str3 = null;

   str1 和 str2 分别运行TextUtils.isEmpty(str) 和 str.isEmpty() 的结果是一样的,结果如下:

TextUtils.isEmpty(str1) == true;
TextUtils.isEmpty(str2) == false;
str1.isEmpty() == true;
str2.isEmpty() == false;

   str3 运行这两个方法的结果却不一样,TextUtils.isEmpty(str3)的结果是TextUtils.isEmpty(str3) == true,而str3.isEmpty()执行时却出现空指针异常,所有str.isEmpty()运行成功的前提是str不能为null。

   这确实是一个小的不能再小的细节问题了,但偶尔也会掉到坑里,使用str.isEmpty()时建议的写法是:

if(str == null && str.isEmpty()){
}

问题二:自定义控件的属性

   这里要记录的是一直在使用自定义属性时的异常问题。在设计自定义控件时,在attrs.xml文件里定义各自的属性,当不同的控件定义了相同的属性时,例如下面这种情况时:


    
        
        
        
        
        
        
        
        
    

    
        
        
        
        
    

此时项目编译会报错,错误如下:
values\attrs.xml: Error: Found item Attr/backgroundColor more than one time
此错误的原因是:属性重复定义,意思就是,不同的控件里分别定义了相同的属性,这时需要把相同的属性提取出来,例如下面的写法:

    
    
    
    
    
    
    
        
        
        
        
        
        
        
        
    
    
    
        
        
        
        
    

这样,错误就没有了,编译也就通过了。

问题三:按键监听中return true 和return false 的区别

return true —— 就是告诉系统,我已经对这个事件处理了,不需要系统再处理了;
return false —— 就是告诉系统,我虽然对这个事件处理了,但是不完善,还需要系统再进行处理。
简单的说就是 return true 把系统对事件的处理拦截掉了,而 return false 没有。

问题四:定时器Timer:Timer was canceled异常问题

   这里记录一个定时器的使用异常过程和解决方法。Timer使用中出现异常,异常信息java.lang.IllegalStateException: Timer was canceled
错误原因是:调用cancel()取消后,timer就取消了,不能再执行 schedule语句。想要继续使用timer的正确方法是:

timer.cancel();
timer.purge();
timer = null;

问题五:使用RecyclerView时,Item不能居中问题

   在一次使用RecyclerView时,发现Item不能居中问题,设置居中的代码也完全无效,一时不能明白是什么原因。网上搜索后找到答案,原来在自定义Adapter里加载布局文件时我用的是下面这种加载布局方式。

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(parent.getContext(), R.layout.item_1, null);
        return new ViewHolder(view);
    }

   把上面的加载方式改成下面的后就可以了:

    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_1, parent, false);
        return new ViewHolder(view);
    }

   具体原因:inflater在inflate一个布局文件时,需要知道parent的类型,才能生成对应的LayoutParams,才可以把布局文件xml根节点的attrs(如layout_width)读进去;如果parent传进去为null,生成的View的LayoutParams为null,在RecyclerView.addView时,发现LayoutParams为null,则生成默认的LayoutParams,则无法实现布局居中的效果。

参考文章:
   RecyclerView中Item不能居中显示
   Android RecyclerView 中的 item 如何居中

问题六:使用ViewPager和FragmentPagerAdapter加载Fragment时,Can't change tag of fragment异常问题

   在一次使用ViewPager和FragmentPagerAdapter循环加载Fragment列表时遇到了Can't change tag of fragment异常问题,查看了源码找到问题所在

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        if (mCurTransaction == null) {
            mCurTransaction = mFragmentManager.beginTransaction();
        }
        final long itemId = getItemId(position);
        // Do we already have this fragment?
        String name = makeFragmentName(container.getId(), itemId);
        Fragment fragment = mFragmentManager.findFragmentByTag(name);
        if (fragment != null) {
            if (DEBUG) Log.v(TAG, "Attaching item #" + itemId + ": f=" + fragment);
            mCurTransaction.attach(fragment);
        } else {
            fragment = getItem(position);
            if (DEBUG) Log.v(TAG, "Adding item #" + itemId + ": f=" + fragment);
            mCurTransaction.add(container.getId(), fragment,
                    makeFragmentName(container.getId(), itemId));
        }
        if (fragment != mCurrentPrimaryItem) {
            fragment.setMenuVisibility(false);
            fragment.setUserVisibleHint(false);
        }
        return fragment;
    }

   看源码知道fragment是通过findFragmentByTag()查找的,跟踪findFragmentByTag方法知道Tag的值和position有关。

   public long getItemId(int position) {
        return position;
    }

    private static String makeFragmentName(int viewId, long id) {
        return "android:switcher:" + viewId + ":" + id;
    }

   在循环加载中,会有不同的position值指向同一个fragment,这就出现了上面的异常。网上有关于该异常的分析和解决方法。这里参考了ViewPager: Can't change tag of fragment这篇文章。这篇文章里也给出了问题的解决方法,可能是因为代码不全的原因,我对上面的方法不是很理解,用上面的方法也没能解决问题。
   这里我只说一下自己的方法,我的方法很简单,既然问题出在FragmentPagerAdapter的instantiateItem(ViewGroup container, int position)里,我自定义MyFragmentPagerAdapter继承FragmentPagerAdapter,然后重写instantiateItem(ViewGroup container, int position)这个方法。而问题的最终原因跟position的值有关,那我给position重新赋值,方法如下

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // 添加下面一行代码修改了“Can’t change tag of fragment”的异常问题
        position = position % mFragmentList.size();
        return super.instantiateItem(container, position);
    }

   这样问题就解决了,是不是很简单,下面是自定义的Adapter全部代码。

MyFragmentPagerAdapter.java

package com.demo.adapter;

import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import android.view.ViewGroup;

import java.util.List;

public class MyFragmentPagerAdapter extends FragmentPagerAdapter {

    private String TAG = "MyFragmentPagerAdapter ";
    private List mFragmentList;

    public MyFragmentPagerAdapter(FragmentManager fm, List fragmentList) {
        super(fm);
        mFragmentList = fragmentList;
    }

    @Override
    public Fragment getItem(int position) {
        return mFragmentList.get(position % mFragmentList.size());
    }

    @Override
    public int getCount() {
        return Integer.MAX_VALUE;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        // 添加下面一行代码修改了“Can’t change tag of fragment”的异常问题
        position = position % mFragmentList.size();
        return super.instantiateItem(container, position);
    }
}

问题七:byte[] 转图片时失败的问题

   在一次使用相机获取图片并保存图片的过程中,byte[] 转图片/图片文件一直不成功,也不报错,折磨了我好一会,不明白为什么这样。后来借鉴网上的思路,对byte[] 进行转码处理,然后再转为图片/图片文件。自己试了一下,确实可以解决问题,虽然原因还是不明确,但也记录一下,以供以后学习参考。

byte[]转为图片文件时的原代码:

private void bytesToImageFile(byte[] data, String path) {
        try {
            File file = new File(path);
            if(file.exists()){
                file.delete();
                file.createNewFile();
            }
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(data, 0, data.length);
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
 }

此段代码运行后能创建图片文件,但图片文件为损坏的文件,无法使用。

byte[]转为Bitmap时的原代码:

private static Bitmap byteToBitmap(byte[] data){
        return BitmapFactory.decodeByteArray(data, 0, data.length);
}

此段代码运行后返回null。

转码处理,方法如下:

/**
 * 转码处理
 * @param data
 * @param width 图片宽度
 * @param height 图片高度
 * @return
 */
private static byte[] transcodeProcess(byte[] data, int width, int height){
        YuvImage yuvImage = new YuvImage(data, ImageFormat.NV21, width, height, null);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        yuvImage.compressToJpeg(new Rect(0, 0, width, height), 80, baos);
        byte[] jdata = baos.toByteArray();
        return jdata;
}

在上面byte转图片文件和Bitmap的过程中,加入转码处理,即可达到预期目的。

byte转图片文件:

private void bytesToImageFile(byte[] data, String path) {
        try {
            File file = new File(path);
            if(file.exists()){
                file.delete();
                file.createNewFile();
            }
            // 转码处理
            byte[] jdata = transcodeProcess(data, 100, 100);
            FileOutputStream fos = new FileOutputStream(file);
            fos.write(data, 0, data.length);
            fos.flush();
            fos.close();
        } catch (Exception e) {
            e.printStackTrace();
        }
 }

byte转Bitmap:

private static Bitmap byteToBitmap(byte[] data){
        // 转码处理
        byte[] jdata = transcodeProcess(data, 100, 100);
        return BitmapFactory.decodeByteArray(jdata , 0, jdata.length);
}

你可能感兴趣的:(Android开发——易犯错误记录)