最近启动了一个新项目叫通用化,老师的设想是这样的:APP里显示的内容和布局都是不确定的,需要从后台传过来的json串中解析出来,然后经过一系列的循环和判断语句,为APP添加上json中要求的布局和内容,就连文字的大小和颜色也都是从后台获取的。
刚开始的时候觉得这样很难实现,但是可能也是可以实现的,不过今天下午遇到了一个世纪大难题,我和实验室另两个同学讨论了半天也没有解决。如果哪天解决了,我就去吃一顿好的,所以先记录一下~
JSON串
先来看看我们要用来确定内容和布局的json,我发誓是我读研以来设计过或者见过的最长的json。大体说分为三层,task,event,work,app要把这三层内容显示出来,而且每个数组里元素的个数都是不确定的,每次从后台获取的都是类似结构但是不同内容的json。
{
"task_ID": "123",
"font_size": 25,
"font_color": "#080808",
"note": [{
"note_name": "任务开始时间",
"note_content": "2018-6-6 12:00:00",
"font_color": "#080808",
"font_size": "20"
}, {
"note_name": "任务结束时间",
"note_content": "2018-6-6 18:00:00",
"font_color": "#080808",
"font_size": "20"
}, {
"note_name": "安装人员",
"note_content": "张三",
"font_color": "#080808",
"font_size": "20"
}, {
"note_name": "任务性质",
"note_content": "普通",
"font_color": "#080808",
"font_size": "20"
}, {
"note_name": "必备工具",
"note_content": "螺丝刀、钳子",
"font_color": "#080808",
"font_size": "20"
}],
"event": [{
"event_ID": "A1",
"event_name": "除氧器",
"font_color": "#6699FF",
"font_size": "20",
"work": [{
"work_ID": "B1",
"work_name": "温度",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "温度拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入温度",
"font_size": 20,
"font_color": "#080808"
}]
}, {
"work_ID": "B2",
"work_name": "仪表参数",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "仪表拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入仪表数字",
"font_size": 20,
"font_color": "#080808"
}]
}]
}, {
"event_ID": "A2",
"event_name": "汽缸",
"font_color": "#6699FF",
"font_size": "20",
"work": [{
"work_ID": "B3",
"work_name": "油温",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "油温拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入油温",
"font_size": 20,
"font_color": "#080808"
}]
}, {
"work_ID": "B4",
"work_name": "电机振动",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "电机拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入电机参数",
"font_size": 20,
"font_color": "#080808"
}]
}]
}, {
"event_ID": "A3",
"event_name": "循环泵房",
"font_color": "#6699FF",
"font_size": "20",
"work": [{
"work_ID": "B5",
"work_name": "温度",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "温度拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入温度",
"font_size": 20,
"font_color": "#080808"
}]
}, {
"work_ID": "B6",
"work_name": "仪表参数",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "仪表拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入仪表数字",
"font_size": 20,
"font_color": "#080808"
}]
}]
}, {
"event_ID": "A4",
"event_name": "给水泵",
"font_color": "#6699FF",
"font_size": "20",
"work": [{
"work_ID": "B7",
"work_name": "温度",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "温度拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入温度",
"font_size": 20,
"font_color": "#080808"
}]
}, {
"work_ID": "B8",
"work_name": "仪表参数",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "仪表拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入仪表数字",
"font_size": 20,
"font_color": "#080808"
}]
}]
}, {
"event_ID": "A5",
"event_name": "疏水泵",
"font_color": "#6699FF",
"font_size": "20",
"work": [{
"work_ID": "B9",
"work_name": "温度",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "温度拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入温度",
"font_size": 20,
"font_color": "#080808"
}]
}, {
"work_ID": "B10",
"work_name": "仪表参数",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "仪表拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入仪表数字",
"font_size": 20,
"font_color": "#080808"
}]
}]
}, {
"event_ID": "A6",
"event_name": "射水泵",
"font_color": "#6699FF",
"font_size": "20",
"work": [{
"work_ID": "B11",
"work_name": "温度",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "温度拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入温度",
"font_size": 20,
"font_color": "#080808"
}]
}, {
"work_ID": "B12",
"work_name": "仪表参数",
"font_color": "#6699FF",
"font_size": "20",
"work_note": [{
"note_name": "正常范围",
"note_content": "10-15",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "异常标准",
"note_content": "过大或过小",
"font_color": "#6699FF",
"font_size": "20"
}, {
"note_name": "特殊提示",
"note_content": "避免烫伤",
"font_color": "#6699FF",
"font_size": "20"
}],
"view": [{
"view_class": "拍照",
"view_name": "仪表拍照",
"font_size": 20,
"font_color": "#080808"
}, {
"view_class": "输入框",
"view_name": "输入仪表数字",
"font_size": 20,
"font_color": "#080808"
}]
}]
}, ]
}
json解析
首先程序中要把json每一层的内容都解析出来,然后显示在相应的位置上。再根据view数组来确定当前页面的布局
JSONArray event_array = test.getJSONArray("event");
for (int i = 0; i < event_array.length(); i++) {
JSONObject event = event_array.getJSONObject(i);
if (event.getString("event_ID").toString().equals(event_ID)) {
JSONArray work_array = event.getJSONArray("work");
for (int j = 0; j < work_array.length(); j++) {
JSONObject work = work_array.getJSONObject(j);
String work_ID = work.getString("work_ID");
String work_name = work.getString("work_name");
addtext(work_ID+work_name, work.getString("font_color"), work.getInt("font_size"));
JSONArray work_note_array = work.getJSONArray("work_note");
for (int k = 0; k < work_note_array.length(); k++) {
JSONObject work_note = work_note_array.getJSONObject(k);
String note_name = work_note.getString("note_name");
String note_content = work_note.getString("note_content");
addtext(note_name + ":" + note_content,
work_note.getString("font_color"), work_note.getInt("font_size"));
}
JSONArray view_array = work.getJSONArray("view");
for (int k = 0; k < view_array.length(); k++) {
JSONObject view = view_array.getJSONObject(k);
String view_name = view.getString("view_name");
String view_class = view.getString("view_class");
addtext(view_name, view.getString("font_color"), view.getInt("font_size"));
if (view_class.equals("拍照")) {
btn_capture = addview_capture();
btn_capture.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Log.v("capture", "-1");
new AlertDialog.Builder(ExecuteActivity.this)
.setTitle("添加图片")
.setItems(addPhoto, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
if (which == 0) {
String haveSD = Environment.getExternalStorageState();
String filename = "$1.jpg";
cameraFile = new File(Environment.getExternalStorageDirectory()+"/"+filename);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cameraFile));
startActivityForResult(intent, 0);
}
if (which == 1) {
//
Intent intent = new Intent();
intent.setType("image/*");//可选择图片视频
//修改为以下两句代码
intent.setAction(Intent.ACTION_PICK);
intent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);//使用以上这种模式,并添加以上两句
startActivityForResult(intent, 1);
Log.v("capture", "0");
}
}
})
.show();
}
});
}else if (view_class.equals("输入框")) {
addview_et2();
}
}
}
}
}
for循环里根据view_class来确定接下来要添加的布局,也就是说调用下面的某一个函数。
//添加文本
private void addtext(String content,String color,int size){
TextView tView = new TextView(this);
tView.setText(content);
tView.setTextColor(Color.parseColor(color));
tView.setTextSize(size);
l.addView(tView);
}
//添加文字输入框布局
private void addview_et2(){
final TextView tv_numleft = new TextView(this);
EditText et = new EditText(this);
l.addView(et);
et.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
number_left = "剩余字数:"+ (maxNum-s.length());
tv_numleft.setText(number_left);
Log.v("YBYZZZZZZZZZZZZZZZ", number_left);
if((maxNum-s.length()<0)){
tv_numleft.setTextColor(ExecuteActivity.this.getResources().getColor(R.color.red));
}else {
tv_numleft.setTextColor(ExecuteActivity.this.getResources().getColor(R.color.black));
}
}
});
}
//添加拍照布局
private ImageButton addview_capture(){
final ImageButton btn_capture = new ImageButton(this);
btn_capture.setBackgroundResource(R.drawable.capture);
//设置图片大小
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(120,
120);
btn_capture.setLayoutParams(params);
l.addView(btn_capture);
return btn_capture;
}
但是这就产生了终极难题!
每次添加的拍照布局中的imagebutton没法动态命名,导致想要显示拍好的照片时,onactivityResult函数里找不到对应的imagebutton。师妹说可能只能通过重写安卓底层的类,修改onactivityResult函数的参数来解决了~
恩,没过几天这个所谓终极难题就解决啦!
方法就是最外层定义一个imagebutton的数组,再定义一个int类型的变量count,每次imagebutton的点击事件里都把count的值更改成当前imagebutton在数组中的位置,然后在onactivityresult里面获取到这个count,就可以把拍好的图片显示在当前点击的imagebutton里了!看下代码
//添加拍照布局
private ImageView addview_capture(){
final ImageView btn_capture = new ImageButton(this);
btn_capture.setImageResource(R.drawable.capture);
btn_capture.setBackgroundColor(Color.parseColor("#FFFFFF"));
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(400,
400);
btn_capture.setLayoutParams(params);
//添加时settag当前的count值
**btn_capture.setTag(count);**
Button button = new Button(this);
LinearLayout.LayoutParams params2 = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT);
params2.gravity = Gravity.RIGHT;
button.setLayoutParams(params2);
button.setText("提交");
l.addView(btn_capture);
l.addView(button);
return btn_capture;
}
调用这个函数后count加1;并在点击事件里传入tag值
if (view_class.equals("拍照")) {
btn_capture[count] = addview_capture();
btn_capture[count].setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
count = (Integer)v.getTag();
new AlertDialog.Builder(ExecuteActivity.this)
.setTitle("添加图片")
.setItems(addPhoto, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
if (which == 0) {
String haveSD = Environment.getExternalStorageState();
String filename = "$1.jpg";
cameraFile = new File(Environment.getExternalStorageDirectory()+"/"+filename);
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cameraFile));
startActivityForResult(intent, 0);
}
if (which == 1) {
//
Intent intent = new Intent();
intent.setType("image/*");//可选择图片视频
//修改为以下两句代码
intent.setAction(Intent.ACTION_PICK);
intent.setData(android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);//使用以上这种模式,并添加以上两句
startActivityForResult(intent, 1);
// Log.v("capture", "0");
}
}
})
.show();
}
});
count = count+1;
onactivityresult函数里的count值已经变成当前imagebutton在数组中的顺位了!
@SuppressLint("ShowToast") protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
case 1:
if (resultCode == Activity.RESULT_OK) {
/**
* 当选择的图片不为空的话,在获取到图片的途径
*/
Uri uri = data.getData();
try {
String[] pojo = { MediaStore.Images.Media.DATA };
Cursor cursor = getContentResolver().query(uri, pojo, null, null, null);
if (cursor != null) {
ContentResolver cr = this.getContentResolver();
int colunm_index = cursor
.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String path = cursor.getString(colunm_index);
/***
* 这里加这样一个判断主要是为了第三方的软件选择,比如:使用第三方的文件管理器的话,你选择的文件就不一定是图片了,
* 这样的话,我们判断文件的后缀名 如果是图片格式的话,那么才可以
*/
String picPath = path;
Toast.makeText(ExecuteActivity.this, picPath, 1000).show();
Bitmap bitmap = BitmapFactory.decodeStream(cr
.openInputStream(uri));
Log.v("capture","1");
btn_capture[count].setImageBitmap(bitmap);
Log.v("capture", bitmap.toString());
btn_capture[count].setScaleType(ImageView.ScaleType.FIT_CENTER);
// btn_capture[y].getBackground().setAlpha(0);
} else {
}
} catch (Exception e) {
}
}
break;
case 0:
if (resultCode == RESULT_OK) {
Toast.makeText(ExecuteActivity.this, "要成功了哦!", 1000).show();
try {
FileInputStream fis = new FileInputStream(ExecuteActivity.cameraFile);
bitmap = BitmapFactory.decodeStream(fis);
fis.close();
} catch (Exception e) {
e.printStackTrace();
}
if (bitmap!=null) {
Log.e(getClass().getName(), (bitmap.getWidth()+","+bitmap.getHeight()));
}
}
}
super.onActivityResult(requestCode, resultCode, data);
}