由于公司的手持是由原生的安卓写的,刚好想了解一下安卓,就开始长达2周的开发过程,5个页面,可能因为跟java有关系,本来是做前端开发的,自己虽然原来学过java,都忘光了...开发初期格外艰难..好在在自己努力,老大和小哥哥的帮助下,开发算是基本圆满完成了,现在来总结一下中间遇到的问题和解决方法。
问题1: 比如数字1.23或者1.26,都进行五入的操作,变成1.3该如何实现?
答: 有提供内置的BigDecimal方法,详情可见https://segmentfault.com/a/1190000002886883,介绍的很详细。
eg:
double value1 = 1.23;
double value2 = 1.25;
BigDecimal bd1 = new BigDecimal(value1);
BigDecimal bd2 = new BigDecimal(value2);
double result1 = bd1.setScale(1, ROUND_UP).doubleValue(); //第一个参数表示保留的小数位数,第二个表示精度取值方式
double result2 = bd2.setScale(1, ROUND_UP).doubleValue();
ROUND_UP: 正数是大于等于该数的那个最近数,负数是小于等于该数的那个最近数
ROUND_DOWN: 与ROUND_UP相反
还有很多其他的精度取值方式...
问题2: EditText控件,默认是获取光标位置,并弹出软键盘。不想要软键盘,但是光标位置希望一直保留闪烁,如何实现?
答:
nameText.setInputType(InputType.TYPE_NULL);
上述代码,会隐藏软键盘,但是也会让光标消失,不可取。
后发现网上有一种根据安卓版本,来判断进行隐藏,看这篇链接:http://www.mamicode.com/info-detail-940579.html
问题3: 如何给listView中的item设置一个固定的高度?
答:
问题4: 布局问题:如何实现左右布局?如何实现上部内容固定,中间部分可滚动,按钮始终固定在底部呢?
答:
对于左右布局,最简单的方法就是,外层布局使用RelativeLayout,包裹的子元素分别设置android:layout_alignParentLeft="true", android:layout_alignParentRight="true"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:textSize="24sp"
android:text="左边" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:textSize="24sp"
android:text="右边" />
</RelativeLayout>
对于后者问题,即布局这样写:即
<RelativeLayout
android:layout_width="wrap_parent"
android:layout_height="wrap_parent">
<TextView
android:id="@+id/nameText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ems="7"
android:gravity="right"
android:textSize="22sp" />
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_parent"
android:layout_height="wrap_parent"
android:layout_below="@+id/nameText"
>
<ScrollView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/nameText"
android:scrollbars="none">
<LinearLayout
android:layout_width="wrap_parent"
android:layout_height="wrap_parent">
XXXXX.....
</LinearLayout>
</ScrollView>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="5dp"
android:orientation="horizontal">
<Button
android:id="@+id/resetbutton"
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:background="@drawable/grey_button"
android:ems="5"
android:gravity="center"
android:text="重置"
android:textColor="@color/white"
android:textSize="24sp" />
<Button
android:id="@+id/confirmbutton"
android:layout_width="100dp"
android:layout_height="40dp"
android:layout_gravity="center"
android:background="@drawable/light_button"
android:ems="5"
android:gravity="center"
android:text="确定"
android:textColor="@color/white"
android:textSize="24sp" />
</LinearLayout>
</RelativeLayout>
即:
(1)最外层是个RelativeLayout布局,中间可滚动部分是个ScrollView,外层包裹一个LinearLayout,为这个LinearLayout设置属性: android:layout_below="@+id/nameText",表示该布局位于该TextView下面。
(2)ScrollView它的里面只能包裹一个子元素,通常是LinearLayout,取消滚动条的显示:android:scrollbars="none"
(3)底部固定的按钮外层包裹LinearLayout,为其设置
** android:layout_alignParentBottom="true"**,就可始终让其居于底部了。
问题5: listView中,如何实现展示一级和二级菜单?类似于这样:

但其实项目的需求是简化了的,不用点击一级菜单,再显示或者隐藏二级菜单,而是直接一次显示所有的一级菜单二级菜单,即像这样:

粉色部分的是一级菜单,褐色部分的是二级菜单?
答:这个问题困扰了我一天多,后来问了技术老大,了解了一下思路就解决了...还是考虑的有问题,思路没有打开啊!
按照上述图示,后台传过来的json数据是这样的:(list里面嵌套list)
{
list: [
{
"name": xxx1,
"usage": xxx1,
"childList": [
{
"childName": xx1
"code":xx1
},
{
"childName": xx2
"code":xx2
}
]
},
{
"name": xxx2,
"usage": xxx2,
"childList": [
{
"childName": xx3
"code":xx3
},
]
}
]
}
本来想使用网上有些教程的:ExpandableListView,但是后来各种报错...只能忍痛弃之
这里还是使用的是listView,里面是listItem。
我刚开始的错误做法是,就是将数据重组,只要遇到childList, 遍历childList里面的数据,并放入map数组中,而数组本身父级的name,id也都塞进这个map数组中。
即最终形成的数据就是这样:
"resetList": [
{
"childName": xx1
"code":xx1,
"name": xxx1,
"usage": xxx1,
},
{
"childName": xx2
"code":xx2,
"name": xxx1,
"usage": xxx1,
},
{
"childName": xx3
"code":xx3,
"name": xxx2,
"usage": xxx2,
},
]
这样在遍历时,本来第一个parent下面应该有2个子元素,第二个parent下面有1个子元素,现在遍历渲染时,即变成这样:

明显不对!要急哭无可奈何的时候,请教了技术老大,老大提供了这样一种思路,就是===》
把父级元素单独提取出来放在一个数组中,把孩子元素提取出来放到一个数组中,在listView重写SimpleAdapter的方法时,进行判断,如果父级元素有,设置父级元素的样式,孩子元素隐藏;如果孩子元素有,就隐藏父级元素,设置孩子元素的样式。
即数据结果变成这样:
"resetList": [
{
"name": xxx1,
"usage": xxx1,
},
{
"name": xxx2,
"usage": xxx2,
},
{
"childName": xx1
"code":xx1,
},
{
"childName": xx2
"code":xx2,
},
{
"childName": xx3
"code":xx3,
},
]
Adapter接收的数据还是传递所有的字段名:
SimpleAdapter simpleAdapter = new abnormalTipsDetailAdapter2(AbnormalTipsDetailActivity.this,
dataList3,
R.layout.abnormal_tips_detail_item2,
new String[]{"name", "usage", "childName", "code"},
new int[]{R.id.name, R.id.usage, R.id.childName, R.id.code}
);
detail_listView.setAdapter(simpleAdapter);
在重写的SimpleAdapter的这个方法getView再进行判断:
public class abnormalTipsDetailAdapter2 extends SimpleAdapter {
List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
private LayoutInflater layoutInflater;
private Context context;
private int hiddenType;
public abnormalTipsDetailAdapter2(Context context, List<? extends Map<String, ?>> data,
int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
this.context = context;
this.data = (List<Map<String, Object>>) data;
this.layoutInflater = LayoutInflater.from(context);
}
public final class abnormalTipsDetailComponent {
public TextView name;
public TextView usage;
public TextView childName;
public TextView code;
public RelativeLayout child; //子组件包裹层
public RelativeLayout topWrapper; //父组件包裹层
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return super.getCount();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return super.getItem(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return super.getItemId(position);
}
public View getView(final int position, View convertView, ViewGroup parent) {
// TODO Auto-generated method stub)
abnormalTipsDetailComponent abnormal_detail_component = null;
if (abnormal_detail_component == null) {
abnormal_detail_component = new abnormalTipsDetailComponent();
//获得组件,实例化组件
convertView = layoutInflater.inflate(R.layout.abnormal_tips_detail_item2, null);
abnormal_detail_component.name = (TextView) convertView.findViewById(R.id.name);
abnormal_detail_component.usage = (TextView) convertView.findViewById(R.id.usage);
abnormal_detail_component.childName = (TextView) convertView.findViewById(R.id.childName);
abnormal_detail_component.code = (TextView) convertView.findViewById(R.id.code);
abnormal_detail_component.topWrapper = (RelativeLayout) convertView.findViewById(R.id.topWrapper);
abnormal_detail_component.child = (RelativeLayout) convertView.findViewById(R.id.child);
convertView.setTag(abnormal_detail_component);
} else {
abnormal_detail_component = (abnormalTipsDetailComponent) convertView.getTag();
}
String name = (String) data.get(position).get("name"); //父组件里才有该值
String childName = (String) data.get(position).get("childName"); //子组件里才有该值
String usage = "";
String code ="";
if(name != null) {
usage = (String) data.get(position).get("usage");
//显示父元素
abnormal_detail_component.topWrapper.setVisibility(View.VISIBLE);
abnormal_detail_component.child.setVisibility(View.GONE);
abnormal_detail_component.name.setText(name);
abnormal_detail_component.usage.setText(usage);
}else if(childName != null) {
code = (String) data.get(position).get("code");
//显示对应的子元素
abnormal_detail_component.topWrapper.setVisibility(View.GONE);
abnormal_detail_component.child.setVisibility(View.VISIBLE);
abnormal_detail_component.code.setText(code + "号");
abnormal_detail_component.code.setTextColor(Color.RED);
}
return convertView;
}
}
这样就解决了。
问题6: listView中的listItem点击每一项时,发现不、无法响应对应的setOnItemClickListener方法?
答:查了百度发现,这个原因是由于:
ListViewItem中有Button或者Checkable的子类控件的话,那么默认focus是交给了子控件,而ListView的Item能被选中的基础是Item本身
所以,分为2步,
(1)为有Button或者Checkable的子类控件增加属性:
android:focusable="false"
android:clickable="false"
android:focusableInTouchMode="false"
(2)在item最外层增加属性:
android:descendantFocusability="blocksDescendants"
问题7: 原生的android开发无论是接收数据还是传递数据,是接收json格式的数据,如何解析呢?
答:这里使用的是插件Gson来解析数据。
在app/build.gradle下,dependencies,加入该插件:

在File/Synchronize后,项目加入该包后,要解析或生成对应的json格式的数据,看这篇链接,https://www.cnblogs.com/liqw/p/4266209.html 写的很详细。
问题8: 页面由一个页面跳转到另一个页面,在原生的android中,使用的是Intent,通过
Intent intent = new Intent();
intent.setClass(MainActivity.this, OtherActivity.class);
startActivity(intent);
finish();
就可以实现跳转,当然这个是未涉及到页面传值,当涉及到传值时,传递的值分别是String,int,或者是一个jsonArray,接收的页面该如何解析获得值呢?
答: 这里,这样实现,先把jsonArray要转化为json字符串形式,再在接收的页面进行解析,代码实现如下:
Intent intent = new Intent();
intent.setClass(MainActivity.this, OtherActivity.class);
String waitAreaList = object.getString("list"); //这里解析jsonObject中的jsonArray的键,获取对应的值
//转化jsonArray为json字符串,项目统一使用的是Gson插件解析json数据
String str = new Gson.toJson(waitAreaList);
//发送数据
intent.putExtra("extra", String.valueOf(123));
intent.putExtra("name","张三");
intent.putExtra("waitAreaList", str);
startActivity(intent);
finish();
//接收数据
Intent intent = getIntent();
int extra = intent.getIntExtra("extra", 0); //获取int类型的数据,为其默认赋值一个0
String name = intent.getStringExtra("name"); //获取string类型的数据
String stringData = intent.getStringExtra("waitAreaList"); //获取json字符串数组
//判断json字符串是否有数据,有数据进行解析,还原成正常的jsonArray
if(stringData.length() != 0) {
//将JsonArray类型的Json字符串解析成对象方法
JsonParser parser = new JsonParser();
JsonArray array = parser.parse(stringData).getAsJsonArray();
....//执行某些方法
}
问题9: 给textView字体加粗?
答: 对于英文字体,直接在对应的xml中,为TextView设置样式
android:textStyle="bold"
而对于中文字体,则要动态的设置:
TextView text = (TextView)findViewById(R.id.text);//或从xml导入
text.getPaint().setFakeBoldText(true); //字体加粗
问题10: 实现类似于ios的那种swithBtn效果?switchBtn放在了listView的item,如何点击时,进行相应,请求接口?
答:
(1)对于问题1:刚开始百度了一堆,刚开始还是想搬砖,模仿网上的demo自定义实现,后来技术老大说,这个东西应该有插件,网上找一下,就不用自己写很多代码了。果断搜索后,发现了插件,github网址:https://github.com/kyleduo/SwitchButton ,例子很详细。
同理,还是现在app/build.gradle下的dependencies加入
compile 'com.github.zcweng:switch-button:0.0.3@aar'
然后在对应要使用的xml实现:
<com.suke.widget.SwitchButton
android:id="@+id/switchBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
app:sb_checked_color="#8BC34A" //开启的背景颜色:绿色
app:sb_enable_effect="true" //动态效果:默认是true,开启
app:sb_show_indicator="false" //是否显示指示器,
app:sb_uncheck_color="#C6C7C5" /> //关闭的背景颜色:灰色
(2)对于问题2,还是考虑了一段时间的。。因为listView的数据源是通过调后端接口动态获得的,因为listItem的文字涉及到动态的改变颜色,所以得重写对应的adapter方法,adapter继承simpleAdapter方法。跟之前问题5的重写Adapter方法是一样的。
问题是,如果在重写的getView方法中,点击对应的switchBtn按钮,进行发起请求,代码会报错...那如何在本身当前页面的Activity中,进行请求?
在listView获取数据后,进行
wait_area_list_view.setAdapter(simpleAdapter);
获取list的子元素,在遍历时,遇到问题,** ListView在setAdapter()后,getChildCount总是0的原因?**
==>网上查到 setAdapater是非同步(asynchronous) 的,所以类似js promise处理这种异步的请求或数据,android也提供方法:加入post函式去更新ListView的ChildView即可。
这篇链接解决了我的问题:
http://www.cnblogs.com/linlf03/archive/2013/06/06/3120408.html
wait_area_list_view.post(new Runnable() { //这里为了解决setAdapter是非同步(asynchronous),取得的childCount总是0的问题
@Override
public void run() {
if (datalist.size() == wait_area_list_view.getChildCount()) {
int childLen = wait_area_list_view.getChildCount(); //获取listView下的item个数
for(int i = 0; i< childLen; i++) {
RelativeLayout layout = (RelativeLayout) wait_area_list_view.getChildAt(i); //获得子item的layout
SwitchButton switchBtn = (SwitchButton) layout.findViewById(R.id.switchBtn); //获得switch按钮
final TextView areaId = (TextView) layout.findViewById(R.id.areaId);
//switchBtn提供当状态更改时的监听器函数
switchBtn.setOnCheckedChangeListener(new SwitchButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(SwitchButton view, boolean isChecked) {
if(isChecked) {
int areaId = Integer.valueOf(areaId.getText().toString()); //获得当前改变状态的id
changeStatus(areaId,isChecked); //进行对应的请求
xxx..
}
}
}
}
}
}
});
//执行对应的后台请求方法
private void changeStatus(int areaId,boolean useState) {
xxx...
}
解决问题~
问题11: 安卓的listView如何获取item的EditText值?(因为:当动态添加数据item或者删除某个item时,由于页面会重绘,导致之前填写的edittext值被重刷消失...)
答: 这个问题还是当时来看,困扰了我半天多,还是最后在小哥哥的帮助和理清思路下,顺利解决了!真希望自己有一天,业务做的久了,在为别人解答疑惑时 都能思路清晰,经验丰富...
首先,要明确一点的是:listView中的item是复用的,就像我们看一个页面中有很多条新闻一样,不是像我们所想象的,有多少个新闻,就有多少个item渲染出来。假设页面有1000条新闻呢?页面会渲染卡死,体验很差,所以按原生的安卓来写的话,所谓复用就是: 假设listView每次就规定渲染10条item,当我们上滑加载更多时,假设此时有3条被遮挡了,那此时要加载后续的数据,安卓的实现是回收前3条被遮挡的item资源,在接下来的展示复用前面的3条,进行显示,周而复始这样循环利用,这样确保了页面的流畅性和性能的提升,不得不说,是一个很棒的思路和做法。
当然,碰到的问题就是如何获取listitem中edittext的值?

好的做法就是: 重写listView的Adapter.getView函数方法时 ,用一个list数组来保存Editext的值,监听EditText值的变化,当值有非空有变化时 ,就将对应的值,存储起来。其中数组的下标就是上述listItem的position值,就是上述图示显示的0,1,2...
当页面比如有数据增加进来或者删除时,我们此时不用担心数据没有,因为预先在一个数组里保存了对应的位置和数据,在页面重绘后,再重新获取list数组中的值,进行对应的setText赋值,这样就保证了数据不会丢失。
这里核心代码主要是如下所示:


博客园的这篇链接: https://www.cnblogs.com/exmyth/p/3799260.html和另外一个链接:http://blog.sina.cn/dpool/blog/s/blog_80c69e390101g221.html 也提供帮助我很多,十分感谢!
问题12: 如何实现自定义dialog?
答:请戳这篇链接:https://blog.csdn.net/zhuwentao2150/article/details/52254579
问题13: 安卓原生系统中,如何让EditText光标集中时,始终隐藏软键盘,但光标不失去焦点?
答: 这次在stackoverflow上找到了答案。
(1)设置EditText的inputType为null,隐藏了软键盘,但同时光标也消失了... 没啥用!
(2) 在AndroiManifest.xml中对应的activity中,设置:android:windowSoftInputMode="stateAlwaysHidden" 然并卵..也没啥用!
(3)当当当~
最终的高票答案来了,应该是可取的,附上代码:
// Update the EditText so it won't popup Android's own keyboard, since I have my own.
EditText editText = (EditText)findViewById(R.id.edit_mine);
editText.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
v.onTouchEvent(event);
InputMethodManager imm = (InputMethodManager)v.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
}
return true;
}
});
解决方法是通过onTouch时,检测input的变化,动态的去显示隐藏软键盘。
这里我未使用的原因是,由于使用的是listview,可能listitem会有许多个,每个item中又有EditText,处理每个时,都进行动态的判断和监听,无疑是比较耗性能的。
所以采取了另外的答案,就是设置在xml这样设置:
<EditText
android:textIsSelectable="true"
...
/>
或者设置:
EditText editText = (EditText) findViewById(R.id.editText);
editText.setTextIsSelectable(true);
对于这个属性,官方给出的解释是:
the cursor will still be present, and you'll be able to select/copy/cut/paste, but SoftKeyboard will never show,但是要求安卓的sdk版本是在>=11以上
这样就解决啦~中间其实还遇到问题,刚开始只是在相应的xml中设置该属性,一台手持设备上表现正常,但另一个页面就光标和软键盘就都消失了。。排查了一圈,发现应该是手持设备sdk的版本问题导致的,所以最终不在xml设置属性,在代码里判断版本,再设置属性:
int version = Build.VERSION.SDK_INT;
EditText editText = (EditText) findViewById(R.id.editText);
if (version >= 11) {
editText.setRawInputType(InputType.TYPE_CLASS_TEXT);
editText.setTextIsSelectable(true);
} else {
editText.setRawInputType(InputType.TYPE_NULL);
editText.setFocusable(true);
}
问题14: listView的listItem中,使用了checkbox控件,由于checkbox的获取焦点优先级高于listItem本身的setOnItemClickListener,所以希望当点击listitem时,也能选中或者不选中对应的checkbox,而不是只能点击checkbox进行选中非选中?
答:这里搜索了一圈,后来有篇博客提供了很好的答案: http://blog.sina.com.cn/s/blog_6fff321b0100otwo.html
即布局大概是这样的:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/check_box_list_item"
android:minHeight="45dp"
android:orientation="horizontal"
android:descendantFocusability="blocksDescendants"
>
<CheckBox xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/boilBox"
style="@style/BoilAreaCheckBox"
android:background="@drawable/selector"
android:button="@null"
android:checked="false"
android:focusable="false"
android:layout_marginTop="5dp"
android:layout_marginLeft="20dp"
/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:textSize="20sp"
android:id="@+id/areaName"
android:layout_marginTop="5dp"
/>
/>
对应的activity中这样写:
checkbox_list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
// LinearLayout layout = (LinearLayout) adapterView.getChildAt(position);
// CheckBox checkBox = (CheckBox) layout.getChildAt(0);
CheckBox checkBox = (CheckBox) view.findViewById(R.id.boilBox);
checkBox.setChecked(!checkBox.isChecked());
}
});
注意:
(1): 这里的 checkbox_list是listView的id名称;
(2): 这里setItem时,去动态监听布局,拿到每次点击的positon对应的LinearLayout; 由于CheckBox控件都是布局的第一个元素,所以是 CheckBox checkBox = (CheckBox) layout.getChildAt(0);
(3): 通过setChecked控制选中,通过isChecked()判断当前是否选中
(4): checkBox获取焦点的优先级,不要抢占点击listitem的优先级,即对于checkbox设置属性:
android:checked="false"
android:focusable="false"
而对于listItem层级,设置属性
android:descendantFocusability="blocksDescendants"
这里在之后发现大bug!!注意注意!!,就是listview中使用checkbox,当数据多时,由于listview本身的复用item的特性,导致getChildAt会报空指针!!
所以之前的代码用斜杠注释掉了,现在点击某一个item下的checkbox,直接在setOnItemClickListener中找到当前view下的checkBox,之后再进行选中非选中操作。
问题15: 如果让edittext的光标始终集中在输入文字的末尾?
答: 添加该行代码:
EditText et = (EditText)R.id.findViewById(R.id.edit);
et.setSelection(et.getText().length()); //让光标在文字末尾
问题16: 当listview中使用了checkbox时,由于listitem的复用的特性,导致勾选的状态混乱或者勾选状态消失,如何解决?
答:虽然这个问题百度和stackoverflow上一搜一大堆,但是质量良莠不齐的,耽误了2天的时间搜索答案,结果中午吃饭的时候突然有了思路,外加查到一篇好的博文,终于解决问题!!大块人心!!
这里好好总结处理一下:
这篇博文: https://blog.csdn.net/jdsjlzx/article/details/7318659 帮助很多!强烈建议一看啊!
我最终的解决方法也是模仿文章的方法来做的:
这里,首先重写Adapter类,继承自SimpleAdapter,这里,用一个HashMap<Integar,Boolean>来保存checkbox的勾选状态,默认初始都为false
public class BindBoidAreaAdapter extends SimpleAdapter {
List<Map<String, Object>> data = new ArrayList<Map<String, Object>>();
private LayoutInflater layoutInflater;
private Context context;
private static HashMap<Integer, Boolean> isSelected = null; //用来控制CheckBox的选中状况
public BindBoidAreaAdapter(Context context, List<? extends Map<String, ?>> data,
int resource, String[] from, int[] to) {
super(context, data, resource, from, to);
this.context = context;
this.data = (List<Map<String, Object>>) data;
this.layoutInflater = LayoutInflater.from(context);
isSelected = new HashMap<Integer, Boolean>();
initStatus();
}
// 初始化isSelected的数据,默认都为未勾选状态
private void initStatus() {
for (int i = 0; i < data.size(); i++) {
getIsSelected().put(i, false);
}
}
//向activity暴露checkbox的勾选状态,返回这个HashMap
public static HashMap<Integer, Boolean> getIsSelected() {
return isSelected;
}
public final class bindBoidAreaComponent {
public CheckBox boilBox;
public TextView areaName;
public TextView areaId;
}
@Override
public int getCount() {
// TODO Auto-generated method stub
return super.getCount();
}
@Override
public Object getItem(int position) {
// TODO Auto-generated method stub
return super.getItem(position);
}
@Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return super.getItemId(position);
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
final int pos = position;
// TODO Auto-generated method stub)
bindBoidAreaComponent bind_boil_area = null;
if (bind_boil_area == null) {
bind_boil_area = new bindBoidAreaComponent();
//获得组件,实例化组件
convertView = layoutInflater.inflate(R.layout.check_box_list_item, null);
bind_boil_area.boilBox = (CheckBox) convertView.findViewById(R.id.boilBox);
bind_boil_area.areaName = (TextView) convertView.findViewById(R.id.areaName);
bind_boil_area.areaId = (TextView) convertView.findViewById(R.id.areaId);
convertView.setTag(bind_boil_area);
} else {
bind_boil_area = (bindBoidAreaComponent) convertView.getTag();
}
bind_boil_area.boilBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener(){
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
getIsSelected().put(pos,isChecked); //用来解决listView在滚动时,checkbox状态混乱的问题
}
});
//绑定数据
String areaName = (String) data.get(position).get("areaName");
Integer areaId = (Integer) data.get(position).get("areaId");
bind_boil_area.areaName.setText(areaName);
bind_boil_area.areaId.setText("" + areaId);
//根据isSelected设置checkbox的选中
bind_boil_area.boilBox.setChecked(getIsSelected().get(pos));
return convertView;
}
}
上述adapter方法中,最重要的几个方法,
(1)一个是构建HashMap,初始化checkbox的state, 方法为initStatus();
(2) 如果要将adapter中设置的HashMap,在activity中 也能使用,则要写一个public static方法,返回这个HashMap,之后就可以在activity中,BindBoidAreaAdapter.getIsSelected(),访问得到我们设置的HashMap。(当时思路就卡在这里了!不知道如果去访问adpater中的设置的这个数据,恩,还是有收获的)
这里就涉及到android的static,是静态变量,一般静态变量用的不多,静态常量用的比较多。
static即始终会保存在内存里,不会随着activity的销毁被回收,会占用内存。static的使用,是为了让多个对象共用一份空间,节省内存,由于不正确的操作或者编码不规范,会造成内存泄露。。但这里先不深入那么多...
(3)特别注意,中间的针对于checkbox的setOnCheckedChangeListener监听的方法中,
getIsSelected().put(pos,isChecked); //该行代码,、在滚动时,动态设置checkbox的选中非选中为true 或者false
和
//根据isSelected设置checkbox的选中
bind_boil_area.boilBox.setChecked(getIsSelected().get(pos));
再来看Activity方法中,接收的数据是这样的,后台会传一份全部的数据list,和(如果之前有勾选checkbox)传递一个checkedList数组,反之传来的checkedList为空字符串。
我这里具体的做法是,请求数据获得全部的数据后,先去动态绘制出这个listview
private void getData() {
xxx....
setAllCheckViews();
//这里前面加判断,如果有勾选的list,则去显示勾选的list
checkbox_list.post(new Runnable() { //这里为了解决setAdapter是非同步(asynchronous),取得的childCount总是0的问题
@Override
public void run() {
checkedBoxView(checkedArray);
}
});
}
//设置全部的checkbox名称与id
private void setAllCheckViews() {
int allcount = allArray.size();
for (int i = 0; i < allcount; i++) {
Map<String, Object> map = new HashMap<String, Object>();
String areaName = allArray.get(i).getAsJsonObject().get("areaName").getAsString();
int areaId = allArray.get(i).getAsJsonObject().get("areaId").getAsInt();
map.put("areaName", areaName);
map.put("areaId", areaId);
allList.add(map);
}
simpleAdapter = new bindBoidAreaAdapter(BindBoilAreaActivity.this,
allList,
R.layout.check_box_list_item,
new String[]{"areaName", "areaId"},
new int[]{R.id.areaName, R.id.areaId}
);
checkbox_list.setAdapter(simpleAdapter);
}
然后,listView中的各个item显示出来后,再进行具体的勾选状态:
private void checkedBoxView(JsonArray checkedArray) {
int allCount = allArray.size();
int checkedCount = checkedArray.size();
for (int i = 0; i < allCount; i++) {
for (int j = 0; j < checkedCount; j++) {
int allId = allArray.get(i).getAsJsonObject().get("areaId").getAsInt();
int checkId = checkedArray.get(j).getAsJsonObject().get("areaId").getAsInt();
if (allId == checkId) { //勾选对应checkbox
BindBoidAreaAdapter.getIsSelected().put(i, true); //这里就是通过双重循环比对id,若id相同,则设置对应的checkbox为选中
}
}
}
simpleAdapter.notifyDataSetChanged(); //重绘一下页面,否则页面观察不到变化!
}
上述代码中,只有通过:
BindBoidAreaAdapter.getIsSelected().put(i, true);
该行代码,BindBoidAreaAdapter这个类才能访问到里面构造的静态方法getIsSelected,从而拿到BindBoidAreaAdapter类中预先定义好的静态变量hashMap,进行对应的更改操作。
但很奇怪,在自己的理解中,在类中定义一个public的方法,按理说不需要static修饰,在activity中实例化这个类的对象,就可以通过对象访问到该方法了。
但当我去掉BindBoidAreaAdapter中对于isSelected这个hashMap的static的修饰,同时也去掉对于方法名getIsSelected的static的修饰时,Activity中
BindBoidAreaAdapter.getIsSelected().put(i, true); 这行代码就会报错,说Non-static method 'getIsSelected' cannot be referenced from a static context.
即后来问了小哥哥,讨论了一下,看书了解到,即只有静态方法可以访问到自身类中的静态域
就比如这个例子
public static int getId() { return nextId; // return static field }
此时假设包含这个静态方法getId的类叫Employee,我们就可以通过
int id = Employee.getId(); //获得数据
可以使用对象调用静态方法,比如我们此时可以
BindBoidAreaAdapter bindBoidAreaAdapter = null; //bindBoidAreaAdapter为BindBoidAreaAdapter的对象
在循环中,再
bindBoidAreaAdapter.getIsSelected().put(i, true);
不过书上讲,这种方式很容易造成混淆,原因是getIsSelected得到的结果跟bindBoidAreaAdapter没有任何关系。
书上建议:
建议使用类名,而不是对象来调用静态方法。
书上同样指出,以下两种情况使用静态方法:
(1)一个方法不需要访问对象状态,其所需参数都是通过显式参数,如Math.pow(x,a),就是一个静态方法
(2)一个方法只需要访问类中的静态域,比如我们之前所写代码的:BindBoidAreaAdapter.getIsSelected().put(i, true);
这里原来自己老的做法是,双重循环遍历,然后
private void checkedBoxView(JsonArray checkedArray) {
int allCount = allArray.size();
int checkedCount = checkedArray.size();
for (int i = 0; i < allCount; i++) {
for (int j = 0; j < checkedCount; j++) {
int allId = allArray.get(i).getAsJsonObject().get("areaId").getAsInt();
int checkId = checkedArray.get(j).getAsJsonObject().get("areaId").getAsInt();
LinearLayout layout = (LinearLayout) checkbox_list.getChildAt(i);
CheckBox checkBox = (CheckBox) layout.findViewById(R.id.boilBox);
if(allId = checkId) {
checkBox.setChecked(true);
}
}
}
}
这里当时就报nullPointerException了,比如现在allArray现在是9个,而页面可视区域只有7个,getChildAt最多定位的postion只有7,当循环遍历到8和9时,listview找不到,就报错了。。。
解决报错的方法:即网上说的很多,positon要为可视区域的position,有2种解决方法:
一种是: 即最外层的循环,allCount并非 allArray.size(),而是listView可视区域的数量,
int allCount = checkbox_list.getLastVisiblePosition() - checkbox_list.getFirstVisiblePosition();
另一种是:内部的getChildAt,使用,
int positon = i - checkbox_list.getFirstVisiblePosition();
LinearLayout layout = (LinearLayout) checkbox_list.getChildAt(positon);
虽然上述2种做法可以解决不报错了,但是还是没有解决勾选对应的应选中的checkbox,还是会复用item,checkbox的状态混乱,所以。。舍弃,但记录下来,为防止以后碰到类似问题。
一个bug解决了2天多...最终有学到东西,还是挺好的,希望自己之后安卓这部分做好,还是回归前端,好好夯实前端基础吧。
问题17: 如何让ScrollView下的内容都能居中显示呢?
答:百度搜了一圈,没有很靠谱的,后来同样是在stackoverflow上找到答案。
问题描述,类似于这样:

好几个优质的答案,我选用的是第一种,即设置linearlayout的中的属性
android:gravity="center_horizontal"
android:layout_gravity="center_horizontal"
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/scrollView1"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingTop="12dp"
android:paddingBottom="20dp"
android:scrollbarStyle="outsideOverlay" >
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center"
android:gravity="center">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="check"
android:gravity="center_vertical|center_horizontal"/>
</LinearLayout>
</ScrollView>
第二种好的做法是,在ScrollView外面再包裹一层Relativelayout,为了让其生效,首先要设置ScrollView下的layout_height:wrap_content,
其次,对于ScrollView,实际上此时为RelativeLayout的子元素,要使其居中,设置属性 android:layout_centerVertical="true"
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ScrollView
android:id="@+id/scrollView1"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true" >
<LinearLayout
android:id="@+id/linearLayout1"
android:layout_width="wrap_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
</LinearLayout>
</ScrollView>
</RelativeLayout>
但个人感觉这样,一层层嵌套包裹,实际上页面渲染会变慢,最好的方式实际上是页面结构越简单越好。
问题18: 遇到一个问题,就是安卓的页面中有EditText控件,在它的下面又有spinner控件,EditText控件 在第一次输入后,按Enter键表现正常,当第二次输入按enter键,则光标会定位到spinner控件上,如何处理?
答: 这里也不知道是不是手持设备的原因,最终是为EditText控件设置属性: android:nextFocusDown="@id/soakEdit",即下次光标定位还是在其本身,就可以解决这个问题了。
<EditText
android:id="@+id/soakEdit"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal"
android:background="@null"
android:ems="8"
android:hint="@string/soak_scan"
android:nextFocusDown="@id/soakEdit"
android:textSize="23sp" />
问题19: 使用原生安卓的button时,button使用的是一张背景图。当发起请求,由于请求是异步的,得到结果有延迟,导致用户以为没有效果,会多点按钮,体验效果差。更好的体验是:发起请求有个loading的状态,得到返回结果后隐藏这个这个loading状态。
答:开始考虑这个问题的时候,跟之前做过的switchButton一样,首先应该考虑的是有无这样的button插件实现。
网上搜到了这种效果的progressButton,但是在设置build.gradle,该插件的依赖时,各种报错,没有办法解决,所以最终弃之。
后来在stackoverflow上,找到提供思路的答案,即使用安卓原生的组件progressBar,具体链接,参考:https://stackoverflow.com/questions/12559461/how-to-show-progress-barcircle-in-an-activity-having-a-listview-before-loading
这个高票答案写的很棒,我也基本上按照这个思路来:

即:
- 先在xml里设置好ProgressBar的布局,默认隐藏;
- 在activity里,初始化该ProgressBar
- 发送请求时,显示这个ProgressBar,得到请求结果时,隐藏该ProgressBar。
中间出现的问题,可能集中在布局这里,原来只是button设置background为drawable下的图片资源,ProgressBar也可以放在按钮的前面,但是问题是分离了,loading未包含在btn里面。
后来好的解决方法:
即将ProgressBar和Button都放在一个Linearlayout布局中,就像这样:
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="@drawable/bh_button_shape"
>
<ProgressBar
android:id="@+id/pbNormal"
android:layout_width="35dp"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:visibility="invisible"
/>
<Button
android:id="@+id/bindbutton"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:background="@android:color/transparent"
android:text="绑定"
android:layout_marginRight="45dp"
android:textColor="@color/white"
android:textSize="24sp" />
</LinearLayout>
具体步骤:
1、为LinearLayout设置背景为原来的button背景图,orientation设置为水平horizontal
2、ProgressBar默认不显示
3、Button设置背景为透明,android:background="@android:color/transparent"
4、具体再调整样式细节。
这中间还遇到一个问题,如何让一个按钮始终居于页面底部呢?
两种方式:
第一种:
如果是Linearlayout布局,则给上面的Linearlayout设置android:layout_weight="1" ,这样可以让底部的button到达底部。
第二种:
使用Relativelayout布局,对于Button设置属性=》,android:layout_alignParentBottom="true"
问题20: 如何让两个TextView在页面上实现左右平分布局?
答:百度查的答案都良莠不齐,后来在stackoverflow上找到答案。
总结一下,有2种方案:
第一种:
用一个Linearlayout包裹这2个TextView,设置layout_width为fill_parent,给子TextView要么设置layout_weight为1要么都不设置这个layout_weight。
代码如下:
<LinearLayout
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
orientation = "horizontal">
<TextView
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:text = "Text 1"
/>
<TextView
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:text = "Text 2"
/>
</LinearLayout>
第二种:(比较高票,我也使用的这个,实现了平分布局的效果)
代码如下:
<RelativeLayout
android:layout_width = "fill_parent"
android:layout_height = "wrap_content">
<LinearLayout
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:orientation="horizontal"
android:weightSum = "2">
<TextView
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:text = "YOUR FIRST TEXT"
android:weight = "1"
/>
<TextView
android:layout_width = "fill_parent"
android:layout_height = "wrap_content"
android:text = "YOUR SECOND TEXT"
android:weight = "1"
/>
</LinearLayout>
</RelativeLayout>
该方法的要点有3个:
- 设置他们的layout_width均为fill_parent
- 给外层包裹的Linearlayout设置权重总和android:weightSum = "2",android:orientation="horizontal"
- 设置子TextView的android:weight = "1"
答案这么介绍这个weight sum属性:
the weight sum will be used to measured the portion of the textview like if you give the weight sum 3 and give 1 textview weight = 2 and another textview weight = 1 the layout will be 2/3 (66.666%) and 1/3 (33.3333%) so if you set it 2 and give every textview weight = 1 it should be 50% and 50%
问题21: 如何将一个JSONArray中的值取出来,添加到一个字符串数组里?如何将一个字符串数组转化为以逗号分隔的字符串?
答:同样是在stackoverflow上找到答案,国外的answer,真是很棒~
这里,原来百度查到的,将字符串数组变为字符串,可以用StringUtils.join(',',strArray);
但后来查到: StringUtils.join(',',strArray) 是Jave 8里添加的一个方法,所以不能用在Android里。
可以用TextUtils.join来代替,
String result = TextUtils.join(", ", list); //list
所以最终所有问题的实现,代码如下可以解决:
ArrayList<String> stringArr = new ArrayList<String>();
JSONArray jsonArray = object.getJSONArray("list");
int count = jsonArray.length();
for(int i = 0; i < count;i++) {
String code = jsonArray.getJSONObject(i).getString("code");
stringArr.add(code);
}
String[] stringArr = stringArr.toArray(new String[stringArr.size()]);
String bucketStr = TextUtils.join(",",stringArr);
问题22: (1)安卓的spinner下拉控件,原来是用一个背景图片替换原生的spinner样式,但是由于固定了宽度等,当文字变多时,文字会被遮挡住,如何优化样式?
(2)如何让spinner下拉选项的文字(默认是靠左的),实现居中显示?
答: 对于(1),(2)找了一圈,后来在stackoverflow上找到答案,spinner此时不用设置background为图片,用定义的样式xml文件实现。具体看代码:
对于问题1:在spinner外包裹一个LinearLayout,为这个LinearLayout设置background
<LinearLayout
android:layout_width="200dp"
android:layout_height="40dp"
android:background="@drawable/bg_spinner"
>
<Spinner
android:id="@+id/print_spinner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@null"
/>
</LinearLayout>
背景样式文件,放在drawable文件下的bg_spinner.xml中
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:top="2dp">
<shape>
<corners android:radius="2dp"/>
<stroke
android:width="2dp"
android:color="#A4A4A4" />
<padding
android:top="2dp"
android:right="2dp"/>
</shape>
</item>
<item android:right="4dp">
<bitmap android:gravity="right|center_vertical"
android:src="@drawable/custom_spinner_icon"
>
</bitmap>
</item>
</layer-list>
注: 第一个item是设置spinner的圆角大小,边框颜色粗细和padding,第二个item是设置旁边的倒着的小三角箭头
对于问题2,如何使spinner下拉选项Item中的文字居中?默认是靠左显示的。
第一步,设置一个xml文件命名为spinner_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:textColor="#929292"
android:textSize="22sp"
android:gravity="center"
android:textAlignment="center"
/>
注意:这里之前设置了属性android:gravity="center",android:textAlignment="center,但是文字就是不居中,查阅了半天,后来发现原来设置android:layout_width和android:layout_height为wrap_content,改为match_parent后就生效了。
第二步,在java类中,调用spinner的时候,使用自定义的这个xml下拉
ArrayAdapter<String> adapter= new ArrayAdapter<String>(context, R.layout.spinner_item, myList);
adapter.setDropDownViewResource(R.layout.spinner_item)