自己开发的项目里,添加了一个简易的“手机号码归属地查询”模块,纯手撸,没有采用第三方框架。
效果图:
开发思路:
界面实现--->获取数据--->数据处理--->界面展示
具体实现:
1. 界面实现
界面绘制比较简单,就不放代码了,其中为EditText做了几点处理:
- 自定义背景
bg_edit_selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@drawable/bg_edit_normal" android:state_focused="false" />
<item android:drawable="@drawable/bg_edit_selected" android:state_focused="true" />
</selector>
bg_edit_normal:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<stroke
android:width="1dp"
android:color="@color/normalBlue" />
<corners android:radius="5dp" />
<solid android:color="@android:color/transparent" />
<padding
android:bottom="10dp"
android:left="12dp"
android:right="12dp"
android:top="10dp" />
</shape>
bg_edit_selected:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/transparent" />
<stroke
android:width="2dp"
android:color="@color/lightBlue" />
<corners android:radius="5dp" />
<padding
android:bottom="10dp"
android:left="12dp"
android:right="12dp"
android:top="10dp" />
</shape>
- 添加动画效果,简单的透明度、伸缩变化
java:
Animation animPhone = AnimationUtils.loadAnimation(this, R.anim.edit_phone_anim);
edtPhone.startAnimation(animPhone);
xml:tv_phone_result_anim
<?xml version="1.0" encoding="utf-8"?>
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromAlpha="1.0"
android:repeatCount="1"
android:repeatMode="reverse"
android:toAlpha="0.3" />
- 点击查询按钮后隐藏键盘
private void hideKeyboard(View view) {
InputMethodManager im = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (im.isActive(view)) {
im.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
}
}
2. 获取数据
- 选用百度的API,写在项目的Consts类里:
public class Consts {
public static final String BAIDU_PHONE_URL = "http://mobsec-dianhua.baidu.com/dianhua_api/open/location";
}
- 自己开辟线程,执行网络请求,读取数据:
class PhoneThread extends Thread {
private String url;
private String number;
private PhoneThread(String url, String number) {
this.url = url;
this.number = number;
}
/**
* 子线程不能直接弹出Toast(其实就是子线程更新UI),方法:
* 1. Looper.prepare(); Looper.loop();
* 2. runOnUiThread(new Runnable(){});
* 3. mHandler.senMessage();
*/
@Override
public void run() {
String result = getJsonFromURL(url);
if (TextUtils.isEmpty(result)) {
Looper.prepare();
Toast.makeText(AttributionActivity.this, R.string.attribution_return_null,
Toast.LENGTH_SHORT).show();
Looper.loop();
} else {
Phone phone = parseJsonToPhone(result, number);
Message msg = Message.obtain();
msg.obj = phone;
mPhoneHandler.sendMessage(msg);
}
}
}
3. 数据处理
- 自定义Phone类(getter()、setter()和toString()略):
public class Phone {
private String number;
private String province;
private String carrier;
private String ownerCarrier;
}
- 解析json:使用Android自带的JSONObject,当然Gson和FastJson更方便
private Phone parseJsonToPhone(String url, String number) {
Phone phone = new Phone();
try {
JSONObject object = new JSONObject(url);
JSONObject numberObj = object.getJSONObject("response").getJSONObject(number);
JSONObject detailObj = numberObj.getJSONObject("detail");
phone.setNumber(number);
phone.setProvince(detailObj.getString("province"));
phone.setCarrier(detailObj.getString("operator"));
phone.setOwnerCarrier(numberObj.getString("location"));
} catch (JSONException e) {
e.printStackTrace();
}
return phone;
}
- 自定义Handler,用于子线程向主线程发送数据,更新UI:
@SuppressLint("HandlerLeak")
class PhoneHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Phone phone = (Phone) msg.obj;
showInfo(phone);
}
}
4. 界面展示
- 查询按钮的点击事件:
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_query:
Animation animPhone = AnimationUtils.loadAnimation(this, R.anim.edit_phone_anim);
edtPhone.startAnimation(animPhone);
/**
* 1.输入不为空则隐藏输入键盘
* 2.获取号码转换为Url
* 3.获取json
* 4.解析json
* 5.更新UI
*/
String url;
String phoneNumber = edtPhone.getText().toString().trim();
if (TextUtils.isEmpty(phoneNumber)) {
Toast.makeText(this, getString(R.string.input_no_empty),
Toast.LENGTH_SHORT).show();
} else {
hideKeyboard(edtPhone);
url = Consts.BAIDU_PHONE_URL + "?tel=" + phoneNumber;
//解析“百度号码归属地API”返回的json数据需要用到电话号码
new PhoneThread(url, phoneNumber).start();
}
break;
default:
break;
}
}
- 真正更新UI的showInfo()方法:
@SuppressLint("SetTextI18n")
private void showInfo(Phone phone) {
tvResult.startAnimation(AnimationUtils.loadAnimation(this, R.anim.tv_phone_result_anim));
tvNumber.setText(phone.getNumber());
tvProvince.setText(phone.getProvince());
tvCarrier.setText(phone.getCarrier());
tvOwner.setText(phone.getOwnerCarrier());
if (phone.getCarrier() == null) {
Toast.makeText(this, R.string.attribution_no_result, Toast.LENGTH_SHORT)
.show();
} else {
switch (phone.getCarrier()) {
case "移动":
civIcon.setImageResource(R.drawable.icon_mobile_phone);
break;
case "电信":
civIcon.setImageResource(R.drawable.icon_telecom_phone);
break;
case "联通":
civIcon.setImageResource(R.drawable.icon_unicom_phone);
break;
default:
break;
}
}
}
完整代码:
/**
* @author: yh
* @date: 2018/5/23 0023
* @function: 手机号归属地查询
*/
public class AttributionActivity extends BaseActivity {
private EditText edtPhone;
private CircleImageView civIcon;
private TextView tvResult;
private TextView tvNumber;
private TextView tvProvince;
private TextView tvCarrier;
private TextView tvOwner;
private PhoneHandler mPhoneHandler = new PhoneHandler();
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_attribution_layout);
initView();
}
private void initView() {
edtPhone = findViewById(R.id.edt_phone);
civIcon = findViewById(R.id.civ_carrier_icon);
tvResult = findViewById(R.id.tv_result);
tvNumber = findViewById(R.id.tv_number);
tvProvince = findViewById(R.id.tv_province);
tvCarrier = findViewById(R.id.tv_carrier);
tvOwner = findViewById(R.id.tv_owner_carrier);
Button btnQuery = findViewById(R.id.btn_query);
btnQuery.setOnClickListener(this);
}
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.btn_query:
Animation animPhone = AnimationUtils.loadAnimation(this, R.anim.edit_phone_anim);
edtPhone.startAnimation(animPhone);
/**
* 1.输入不为空则隐藏输入键盘
* 2.获取号码转换为Url
* 3.获取json
* 4.解析json
* 5.更新UI
*/
String url;
String phoneNumber = edtPhone.getText().toString().trim();
if (TextUtils.isEmpty(phoneNumber)) {
Toast.makeText(this, getString(R.string.input_no_empty),
Toast.LENGTH_SHORT).show();
} else {
hideKeyboard(edtPhone);
url = Consts.BAIDU_PHONE_URL + "?tel=" + phoneNumber;
//解析“百度号码归属地API”返回的json数据需要用到电话号码
new PhoneThread(url, phoneNumber).start();
}
break;
default:
break;
}
}
class PhoneThread extends Thread {
private String url;
private String number;
private PhoneThread(String url, String number) {
this.url = url;
this.number = number;
}
/**
* 子线程不能直接弹出Toast(其实就是子线程更新UI),方法:
* 1. Looper.prepare(); Looper.loop();
* 2. runOnUiThread(new Runnable(){});
* 3. mHandler.senMessage();
*/
@Override
public void run() {
String result = getJsonFromURL(url);
if (TextUtils.isEmpty(result)) {
Looper.prepare();
Toast.makeText(AttributionActivity.this, R.string.attribution_return_null,
Toast.LENGTH_SHORT).show();
Looper.loop();
} else {
Phone phone = parseJsonToPhone(result, number);
Message msg = Message.obtain();
msg.obj = phone;
mPhoneHandler.sendMessage(msg);
}
}
}
@SuppressLint("HandlerLeak")
class PhoneHandler extends Handler {
@Override
public void handleMessage(Message msg) {
Phone phone = (Phone) msg.obj;
showInfo(phone);
}
}
private String getJsonFromURL(String urlStr) {
StringBuilder sb = new StringBuilder();
try {
InputStream is = new URL(urlStr).openStream();
InputStreamReader isr = new InputStreamReader(is);
BufferedReader br = new BufferedReader(isr);
String line;
while ((line = br.readLine()) != null) {
sb.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
Log.d("yh", "getJsonFromURL: " + sb.toString());
return sb.toString();
}
/**
* 将获取的json字符串转换为Phone对象
*
* @param url
* @param number
* @return
*/
private Phone parseJsonToPhone(String url, String number) {
Phone phone = new Phone();
try {
JSONObject object = new JSONObject(url);
JSONObject numberObj = object.getJSONObject("response").getJSONObject(number);
JSONObject detailObj = numberObj.getJSONObject("detail");
phone.setNumber(number);
phone.setProvince(detailObj.getString("province"));
phone.setCarrier(detailObj.getString("operator"));
phone.setOwnerCarrier(numberObj.getString("location"));
} catch (JSONException e) {
e.printStackTrace();
}
return phone;
}
/**
* 在页面上显示Phone对象的相关信息
*
* @param phone
*/
@SuppressLint("SetTextI18n")
private void showInfo(Phone phone) {
tvResult.startAnimation(AnimationUtils.loadAnimation(this, R.anim.tv_phone_result_anim));
tvNumber.setText(phone.getNumber());
tvProvince.setText(phone.getProvince());
tvCarrier.setText(phone.getCarrier());
tvOwner.setText(phone.getOwnerCarrier());
if (phone.getCarrier() == null) {
Toast.makeText(this, R.string.attribution_no_result, Toast.LENGTH_SHORT)
.show();
civIcon.setImageResource(R.drawable.icon_question_phone);
} else {
switch (phone.getCarrier()) {
case "移动":
civIcon.setImageResource(R.drawable.icon_mobile_phone);
break;
case "电信":
civIcon.setImageResource(R.drawable.icon_telecom_phone);
break;
case "联通":
civIcon.setImageResource(R.drawable.icon_unicom_phone);
break;
default:
break;
}
}
}
private void hideKeyboard(View view) {
InputMethodManager im = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (im.isActive(view)) {
im.hideSoftInputFromWindow(view.getApplicationWindowToken(), 0);
}
}
private Phone parseJsonByFastJson(String t) {
return new Phone();
}
private Phone parseJsonByGson(String t) {
return new Phone();
}
}