这里是作者(OCN.Yang)在Android初级阶段遇到的那些坑坑洼洼,有些还是开发中要知道的小技巧。相信大多数初学者难免也会遇到相同的坑,大家大概看看有者避之,还没遇到的就躲之。大牛和已经进阶的朋友可以绕道(相信你们很忙的)。
这篇博客首发在我的个人博客网站 www.ocnyang.com
原文地址
查看代码的大纲 即类的方法列表。
可以给背景设置一个null值,在一定情况下这样做是有必要的。
TextUtils.isEmpty(String str)
当LinearLayout布局中设置orientation="vertical"属性:
设置为单实例模式(singleInstance)的Activity,会单独开一个任务栈单独存放这个activity,这个任务栈只会在程序退出后消除。
Intent intent = new Intent();
/* 开启Pictures画面Type设定为image */
intent.setType("image/*");
/* 使用Intent.ACTION_GET_CONTENT这个Action */
intent.setAction(Intent.ACTION_GET_CONTENT);
/* 取得相片后返回本画面 */
startActivityForResult(intent, 1);
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
Uri uri = data.getData();
ContentResolver cr = this.getContentResolver();
try {
Bitmap bitmap = BitmapFactory.decodeStream(cr.openInputStream(uri));
ImageView imageView = (ImageView) findViewById(R.id.iv01);
/* 将Bitmap设定到ImageView */
imageView.setImageBitmap(bitmap);
} catch (FileNotFoundException e) {
Log.e("Exception", e.getMessage(),e);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
主要方法:
R文件的编译原理:当资源文件或id命名中其中一个文件的命名不满足规范时,R文件整体就不会再编译。
注意: Spinner:如果item中有能够获得焦点的控件(例如CheckBox),则在item获得点击事件之后会继续传递给能够获得焦点的控件.
想让item在被点击后Spinner收回去,需要在布局(例如LinearLayout)中加上属性后代是否可以获得焦点:
android:descendantFocusability="blocksDescendants"//阻止后代获得焦点
getResources().getStringArray(R.array.city)
Java代码
System.out.println(":ab:cd:ef::".split(":").length);//末尾分隔符全部忽略
System.out.println(":ab:cd:ef::".split(":",-1).length);//不忽略任何一个分隔符
System.out.println(StringUtils.split(":ab:cd:ef::",":").length);//最前面的和末尾的分隔符全部都忽略,apache commons
System.out.println(StringUtils.splitPreserveAllTokens(":ab:cd:ef::",":").length);//不忽略任何一个分隔符 apache commons
输出:
4
6
3
6
看了下jdk里String类的public String[] split(String regex,int limit)方法,感觉平时不太会用这方法,以为在用正则表达式来拆分时候,如果匹配到的字符是最后一个字符时,会拆分出两个空字符串,
例如"o"split("o",5) or "o"split("o",-2)时候 结果是"" "" 也就是下图中红框里的内容,所以平时一般都用split(String regex) 方法,其实也就等同于split(String regex,0)方法,把结尾的空字符串丢弃!
xListView上滑刷新,下滑加载更多///swapListView侧滑删除/
只能inflatay一次。不然会报错。
以图片的左上角为坐标(0,0),分别计算出pivotX和pivotY的数值:50%是图片本身大小的一半,50%p是父窗体宽高的一半长度。然后在图片的左上角的基础上加上这两个数组
位移也是同样的计算方式
ButterKnife Gosn android.selector.generat
android 中的控件的margin 和 padding 都是不会影响控件的设置宽高。(这点和网页设计是不一样的)
当布局或id报找不到的错时,可能就是R文件出错。
自定义ListView中,如果item采用多种类型的布局。那么在getItemType中的下标一定要从0开始。不然会报下标越界异常。
public static void main(String[] args) {
String str = "dsijiswer*dfhjgf
sdsd
";
Pattern p = Pattern.compile("");
Matcher m = p.matcher(str);
while (m.find()) {
System.out.println(m.group());
}
}
在drawer文件夹下创建一个shape的图形文件
在linearLayout里设置属性divider为上图形,同时设置showdivider属性
txtZQD = (TextView) findViewById(R.id.txtZQD);
Drawable[] drawable = txtZQD.getCompoundDrawables(); //获取它前后左右的图片
// 数组下表0~3,依次是:左上右下
drawable[1].setBounds(100, 0, 200, 200);
txtZQD.setCompoundDrawables(drawable[0], drawable[1], drawable[2],drawable[3]);
在加载布局的时候要用 View.inflate(context,R.layout.img_share,this);
或者 LayoutInflater.from(context).inflate(R.layout.img_share,this);
不能用 LayoutInflater.from(context).inflate(R.layout.img_share,null);这样加载不上。
配置错误原因:1、导包有误。2、少导包。3、重复导包。
Scrollview里嵌套Gridview,Gridview抢焦点问题(显示布局的时候老是先从Gridview的第一个item显示)
解决方法:在获取inflate view之后,代码给GridView.setFocusable(false)就可以了
这里的listview的item里嵌套了横向滑动的scrollview的gridview,
同样的问题,同样的解决方案:在listview的adapter中在加载完item之后对gridview设置setFocusable(false)。
这里需要注意的是,是在抢焦点的列表的父容器建立之后就对列表设置取消焦点。
例如:
在ListView中嵌套GridView中:
convertView = LayoutInflater.from(mContext).inflate(R.layout.item_listview_home, null);
viewHolder = new ViewHolder(convertView);
viewHolder.mGridIilh.setFocusable(false);
在fragment布局中存在列表抢占焦点:
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_cheapsale, null);
ButterKnife.inject(this, view);
mGridFragmentCheapsale.setFocusable(false);
在Activity布局中存在列表抢占焦点:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sale);
ButterKnife.inject(this);
mListviewHome.setFocusable(false);
在gridview和listview初始化数据时自动调用或者我们显示调用notifyDataSetChanged的时候第一个item会被选中并会抢焦点。
android4.4在调用notifyDataSetChanged的时候注释掉了判断touchmode的代码,导致一调用notifyDataSetChanged就模拟用户点击了gridview。
我们继承gridview或者listview重写里面的isInTouchMode方法:
[java] view plaincopy
/**
* 屏蔽android4.4 setAdapter时View抢焦点的BUG
*/
@Override
public boolean isInTouchMode() {
if(19 == Build.VERSION.SDK_INT){
return !(hasFocus() && !super.isInTouchMode());
}else{
return super.isInTouchMode();
listview默认背景和系统窗口一样是透明的,如果给listview加上背景图片,或者背景颜色时,滚动时listview会黑掉,因为滚动时,列表里面的view重绘用的依旧是系统默认的透明色,颜色值为#FF191919
解决办法:
Intent intent = new Intent();
intent.setClassName("com.android.settings","com.android.settings.ManageApplications");
intent.setAction("android.intent.action.MAIN");
try {
startActivity(intent);
} catch (Exception e) {
e.printStackTrace();
}
解决之道:在EditText的父级控件中找一个,设置成
android:focusable="true"
android:focusableInTouchMode="true"
这样,就把EditText默认的行为截断了!
如果RadioGroup中设置默认选中一个RadioButton后,在选择时会选中两个的问题
解决方法:
不需要设置RadioButton的默认选中, 这样会使RadioButton一直处于选中状态.
我们应该给RadioGroup设置选中的RadioButton,
也就是说把radioButton.setCheck(true);
更改为radioGroup.check(radioButton.getId());
1、自定义ImageView重写View的onMeasure方法
public class ResizableImageView extends ImageView {
public ResizableImageView(Context context) {
super(context);
}
public ResizableImageView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){
Drawable d = getDrawable();
if(d!=null){
// ceil not round - avoid thin vertical gaps along the left/right edges
int width = MeasureSpec.getSize(widthMeasureSpec);
//高度根据使得图片的宽度充满屏幕计算而得
int height = (int) Math.ceil((float) width * (float) d.getIntrinsicHeight() / (float) d.getIntrinsicWidth());
setMeasuredDimension(width, height);
}else{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
}
2、设置ImageView的属性
//宽度填满屏幕
android:layout_width=”match_parent”
android:scaleType=”fitXY”
android:layout_height=”wrap_content”
//保持比例,一定要设置
android:adjustViewBounds=”true”
因为Glide加载图片的规则是根据imageview的大小调整图片。但是ImageView的大小为ImageView宽度填满屏幕,高度自适应的时候,Glide加载的图片就会显示不出来,为此我们选择了一种迂回的方式加载:先请求图片为bitmap,这个时候图片就有一定的尺寸了,再设置到ImageView中就可以自适应了:
Glide.with(GraphicDetailsFragment.this)
.load((new StringBuffer(Const.URL_HEAD).append(mStringList.get(position))).toString())
.asBitmap()
.into(new SimpleTarget() {
@Override
public void onResourceReady(Bitmap resource, GlideAnimation super Bitmap> glideAnimation) {
viewHolder.mImageView.setImageBitmap(resource);
}
});
设置GridView的android:listSelector属性
android:listSelector="@null" 设置后四周空隙就消除了
1.设置item的行间距:
可以在xml布局文件中的listView下设置xml属性:
android:divider="#00000000"
android:dividerHeight="18dp"
解释:分隔线透明,高度为18dp。
2.去掉item之间的分割线:
每个item之间都有分割线,如果单纯想去掉分割线,方法还是很多的:
3.隐藏头部分隔线
listview分割线会在头部、数据item、及根部的底部打印,如果要取消头部分割线必须
先设置其方法
addHeaderView(headView, null, true);
addFooterView(footView, null, true);
注意:第三个参数必须为true,否则无效
//显示头部出现分割线
listview.setHeaderDividersEnabled(true);
//禁止底部出现分割线
listview.setFooterDividersEnabled(false);
今天android stutio出现Error:Failed to create directory'C:\Users\Administrator\.gradle\caches\2.8\scripts\ijinit7_5jx13p26aqkoramvuhfn0lqca\init\classes'
这个问题,解决的办法是:File
下面有个 Invalidate Caches/Restart
,弹出一个框,点击 Invalidate and Restart
按钮,等待重启就OK了。
TextView.append(CharSequence text);//在现有字符串的基础上向字符串缓存区追加字符串;(自己想法:可能会在刷新页面的时候造成多次重复追加,所以不提倡使用)
疑问:两个嵌套,fragment给第二个Fragment通过setArguments(bundle)传值,传递不过去。
setScrollbarFadingEnabled(true);
//不活动的时候隐藏,活动的时候显示
setVerticalScrollBarEnabled(true);
//不活动的时候隐藏,活动的时候也隐藏
android: scrollbars="none"与
setVerticalScrollBarEnabled(true);
效果一样。
重写ListView的onMeasure的方法来解决滑动冲突的应用中,ListView是不能设置divider和高度的,如果设置了,最后一个item显示不全,这是因为onMeasure在根据条目设置ListView的高度时并没有把divider的高度设置进去。
Error:Cannot change dependencies of configuration ':app:_debugAnnotationProcessor' after it has been resolved.
当导入一个第三方库的时候报的错误。是因为这个库依赖的某些库版本过高而你的Android studio没有下载这个库。
ScrollView中没有match_parent这么一说,相当于它内部的控件高度是没有参考物的。match_parent的失效,它会表现成为wrap_content。
在android中创建菜单menu时需要重写Activity的onCreateOptionsMenu(Menumenu)方法,这个方法只在第一次创建的时候调用一次,所以如果之后想对menu进行动态的修改,那么就不能再对onCreateOptionsMenu做什么手脚,就要用到onPrepareOptionsMenu(Menumenu)方法了。
onPrepareOptionsMenu与onCreateOptionsMenu不同的是,他在每次按下menu硬键之前会被调用,所以可以在这里动态的改变menu。
注意:在onPrepareOptionsMenu(Menumenu)函数中,首先需要调用:
super.onPrepareOptionsMenu(menu);
menu.clear();
如果没有clear而直接add的话,那么菜单中菜单项是会“追加”的,这样随着你不停的点menu键,菜单项就不停的增加。
另外,android系统默认的菜单样式是支持最多3个一行,如果有4项就每行2个有2行...如果想自定义样式,可以使用xml文件定义样式。
一个Activity在manifet里声明了Android:parentActivityName;这时候通过Activity左上角的返回按钮点击返回,启动声明的父Activity,并且总会先调用父Activity的OnDestroy方法,点击子Activity的左上角返回按钮的时候,调用逻辑如下:
MainActivity.onDestroy();
MainActivity.onCreate(null);
MainActivity.onStart();
解决方案是:
为设置MainActivity属性android:launchMode=singleTop (这个大概的意思是,消除先调用Destroy方法的解决方案)
顺便脑补android:parentActivityName的作用,就是为了左上角给子Activity加一个返回按钮,具体信息如下:
Android 4.1 提高性能、增强用户体验
App 栈导航:通过设置 android:parentActivityName 改变回退栈的内容,如果栈中没有 parentActivity,则合成栈,通过
onPrepareNavigateUpTaskStack() 改变 parentActivity 中的内容。
条件编译(C里面的概念)是个好东西,但是在java体系中却没有这样的预定义。但是我们可以根据java编译时对代码的优化机制实现条件编译。
java在编译中有这样一条原则:“编译器会对代码进行优化,对于条件永远为false的语句,JAVA编译器将不会对其生成字节码。”
这样一来,我们只需要在Const(静态变量类)中定义一个isDebug的布尔变量,然后对想要进行条件编译的代码用
if(isDebug){
...代码语句...
}
包含起来。这样你可以通过控制isDebug的值实现条件编译。当isDebug为false时,if条件内的...代码语句...会被编译器忽略,也就是不会生成对应的字节码。
//实现的原理是 java的反射?
int resId = (Integer) R.drawable.class.getField("icon").get(null);
holder.img.setImageResource(resId);
需要明白的一点就是 每一张图片的name 都是 R.drawable的一个字段
在Values下创建数组文件:
- 0xFF79429D
- 0xFF5691F5
- 0xFF76BA55
- 0xFFB3E0E0
- 0xFF27C9C2
- 0xFF92AD6B
- 0xFFEC4F4F
- 0xFFEE6593
- 0xFFFFAC5A
- 0xFF6296AF
- 0xFFBF8A28
- 0xFF319388
这里需要注意的是颜色的数值必须是8位,即前两位的透明位不能省略。
然后就可以获取
Resources resources = context.getResources();
mColorArray = resources.getIntArray(R.array.itemcolor);
...
setBackgroundColor(R.id...., mColorArray[Position % mColorArry.length]);
有时候 ScrollView 嵌套 LinearLayout 时会有显示不全的情况,这个时候应该看看 ScrollView 的父布局是不是用的 CoordinatorLayout。
当外部用 CoordinatorLayout 时,内部应该用 NestedScrollView ,不然会产生不确定的 bug。
同样在 CoordinatorLayout 内部使用 ViewPager & TabLayout 来显示不同的 Fragment 时,如果 Fragment 内使用 ScrollView 也会有显示不全的情况。
double sin20 = Math.sin(Math.PI * 20 / 180); //0.3420201433256687 double sin201 = Math.sin(20 / 180 * Math.PI); //0.0
找到原因了,当先计算20/180的时候是按int类型运算的,得到的结果是0。所以最后结果为0.
Android4.0 以上 AlertDialog 在触摸对话框边缘外部,对话框消失
可以设置这么一条属性,当然必须先AlertDialog.Builder.create()之后才能调用这两个方法
方法一:setCanceledOnTouchOutside(false);
调用这个方法时,按对话框以外的地方不起作用。按返回键还起作用
方法二:setCancelable(false);
调用这个方法时,按对话框以外的地方不起作用。按返回键也不起作用
当你的项目中用到用 Vector 图形时,请注意事项可以参考http://www.jianshu.com/p/e3614e7abc03
另外值得注意的是
//mDrawableActive = ContextCompat.getDrawable(context, R.drawable.vec_checkbox_fill_circle_outline);
//mDrawable = ContextCompat.getDrawable(context, R.drawable.vec_checkbox_blank_circle_outline);
//解决 vector “资源未找到” 错误,可以考虑用一下方法代替
try {
mDrawableActive = AppCompatDrawableManager.get()
.getDrawable(context, R.drawable.vec_checkbox_fill_circle_outline);
mDrawable = AppCompatDrawableManager.get()
.getDrawable(context, R.drawable.vec_checkbox_blank_circle_outline);
} catch (Resources.NotFoundException notFoundException) {
Logger.e(notFoundException.getMessage());
} catch (Exception e) {
Logger.e(e.getMessage());
} finally {
mDrawableActive = context.getResources().getDrawable(R.drawable.vec_checkbox_fill_circle);
mDrawable = context.getResources().getDrawable(R.drawable.vec_checkbox_empty_circle);
}
同时需要注意的是:使用Vector时好像不能关闭硬件加速:
android:hardwareAccelerated="false"//不要在配置文件中使用这个设置
当你在测试应用时,如果在部分机型上运行正常,而在部分机型上出现 OOM ,除了对应用再次做各种优化外,你可以在配置文件中加上:
说不定就好了呢。
引起原因:
在V7包下,Wiget.AppCompat.Toolbar的parent中,contentInsetStart(默认的有值)这个属性就是引起自定义ActionBar不能完全填充的原因。
然后在AppStyle中(一定在这,单独在Toolbar的style中设置不起作用)重写Toolbar的属性:
- @style/ClubToolbar
DrawerLayout.setDrawerListener(new ActionBarDrawerToggle);//有动画效果的菜单图标
DrawerLayout.setScrimColor(Color.TRANSPARENT) //去除侧滑时的阴影遮罩效果
在一定版本中的NavigationView的都是有半透明效果的
如果不需要可以通过设置下面属性去除:
app:insetForeground="@android:color/transparent"
同时如果想要Nav在使用中没有背景全透明直接设置背景为#00000000;同时去除 headerLayout 的背景即可。
使用 SDK:15~25
测试版本:Android 6.0;Android 4.3(在这两个版本上都存在,中间版本一定也存在)
一次无意中发现的。如果给 TextView 设置一个 inputType 属性,按理说,inputType 应该是不起作用的,但是这里将会存在一个神奇的事情(不知道算不算一个原生bug):
android:overScrollMode="never"
android:inputType="textMultiLine" //可以显示多行
android:gravity="left|top" //输入时光标左上角
android:minLines="6" //最小显示6行
左边和上边的黑边表示拉伸区域。右边和下边的黑边表示填充区域
其实,NavigationView是一个RecyclerView(在23.1.0版本之前是ListView),header布局通常是0号元素。在Support Library v23.1.1版本中,可以使用如下方法很方便地获取到header中的view:
View headerLayout = navigationView.getHeaderView(0); // 0-index header
而在23.1.0版本中,就需要通过这种方法:
View headerLayout =
navigationView.inflateHeaderView(R.layout.navigation_header);
panel = headerLayout.findViewById(R.id.viewId);
// panel won't be null
限制只输入某些值包括数字、字母等
android:digits="0123456789abcdefghigklmnopqrstuvwxyz"
上面这行代码可以是你任何的限制,只能输入什么就到里面写就可以了,上面写的是只可以输入数字和字母。
android:inputType="textPassword"
android:digits="0123456789abcdefghigklmnopqrstuvwxyz"
这里是只能输入数字和字母并且是密码格式,这两个设置并没有冲突, android:inputType="textPassword"
这里是文本,是可以输入中文的,但是要是你加了android:digits="0123456789abcdefghigklmnopqrstuvwxyz"
,inputType 这里的中文就会失效了,但是 password 不会失效,这里只是打个比方,大家都懂的。
inputType的属性值
android:inputType="none"
android:inputType="text"
android:inputType="textCapCharacters" 字母大写
android:inputType="textCapWords" 首字母大写
android:inputType="textCapSentences" 仅第一个字母大写
android:inputType="textAutoCorrect" 自动完成
android:inputType="textAutoComplete" 自动完成
android:inputType="textMultiLine" 多行输入
android:inputType="textImeMultiLine" 输入法多行(如果支持)
android:inputType="textNoSuggestions" 不提示
android:inputType="textUri" 网址
android:inputType="textEmailAddress" 电子邮件地址
android:inputType="textEmailSubject" 邮件主题
android:inputType="textShortMessage" 短讯
android:inputType="textLongMessage" 长信息
android:inputType="textPersonName" 人名
android:inputType="textPostalAddress" 地址
android:inputType="textPassword" 密码
android:inputType="textVisiblePassword" 可见密码
android:inputType="textWebEditText" 作为网页表单的文本
android:inputType="textFilter" 文本筛选过滤
android:inputType="textPhonetic" 拼音输入 //数值类型
android:inputType="number" 数字
android:inputType="numberSigned" 带符号数字格式
android:inputType="numberDecimal" 带小数点的浮点格式
android:inputType="phone" 拨号键盘
android:inputType="datetime" 时间日期
android:inputType="date" 日期键盘
android:inputType="time" 时间键盘
问题描述:
开发中,遇到退出登录时,需要将界面跳转到登录界面,并将栈中所有Activity清空。
解决办法
Intent intent = new Intent(A.this,B.class).setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);
startActivity 的时候传递 FLAG_ACTIVITY_CLEAR_TASK
这个标志,那么这个标志将会清除之前所有已经打开的 activity
.然后将会变成另外一个空栈的 root ,然后其他的 Activitys 就都被关闭了.这个方法必须跟着 {@link #FLAG_ACTIVITY_NEW_TASK}
一起使用.
inflate(int resource, ViewGroup root, boolean attachToRoot)
大大小小的坑,无处不在,防不胜防。后续会继续更新这篇博文的。
点击查看更多Android技术文章
本博客是作者(OCN.Yang)原创
转载请标明原地址:http://ocnyang.com/2016/08/31/AndroidGu/