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