上一篇:Android 天氣APP(二十七)增加地圖天氣的逐小時天氣、太陽和月亮資料
開發流程
- 一、前情提要
- 二、修改布局和優化業務
效果圖

一、前情提要
??在我寫完地圖天氣之后就有一種如釋重負的感覺,但是這種感覺沒有保持多久,就被新的需求功能所取代,因為我會讓我身邊的朋友幫忙測驗使用,并提一些建議,我來決定是否汲取,這一次我收到了一個很好的建議,所以就有了這一篇文章,當然在我寫的時候,功能就已經是完成了的,需求是這樣的,之前的地圖是通過手動點擊地圖然后定位到某一個點,然后獲取天氣資訊,那么很多人一進入這個頁面并不知道地圖可以點擊,那么這個時候該怎么去定位呢?于是就想到有一個地方能夠讓用戶去輸入,輸入城市名之后,定位到這個城市,然后獲取城市天氣,功能就是這樣,說起來是比較簡單的功能,但是做起來可就不那么容易了,因為我是比較在意用戶體驗的,所以有的地方比較的執著,至于為什么?進入正題吧,
二、修改布局和優化業務
先來優化業務吧,
進入MapWeatherActivity找到onGetReverseGeoCodeResult方法回傳

這樣就算是優化了一下下,OK開始修改布局了,布局會用到一個背景樣式和兩個圖示
shape_search_bg.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="24dp"/>
<solid android:color="@color/white"/>
</shape>
圖示如下:



下面來看看新增的布局具體的代碼
<!--頂部搜索布局-->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="right"
android:paddingLeft="@dimen/dp_12"
android:paddingTop="@dimen/dp_28"
android:paddingRight="@dimen/dp_12">
<RelativeLayout
android:id="@+id/lay_search"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="right"
android:background="@drawable/shape_search_bg">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_search"
android:layout_width="36dp"
android:layout_height="36dp"
android:padding="6dp"
android:src=https://blog.csdn.net/qq_38436214/article/details/"@mipmap/icon_search" />
<EditText
android:id="@+id/ed_search"
android:layout_width="@dimen/dp_0"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="@null"
android:ems="8"
android:hint="輸入城市名"
android:imeOptions="actionSearch"
android:padding="@dimen/dp_4"
android:singleLine="true"
android:textColor="@color/black"
android:textCursorDrawable="@drawable/cursor_style"
android:textSize="@dimen/sp_14"
android:visibility="gone" />
<ImageView
android:id="@+id/iv_close"
android:layout_width="36dp"
android:layout_height="36dp"
android:padding="8dp"
android:src=https://blog.csdn.net/qq_38436214/article/details/"@mipmap/icon_close"
android:visibility="gone" />
</LinearLayout>
</RelativeLayout>
</LinearLayout>
還是比較簡單的,然后進入到MapWeatherActivity
ImageView ivSearch;//搜索圖示
@BindView(R.id.ed_search)
EditText edSearch;//搜索輸入框
@BindView(R.id.iv_close)
ImageView ivClose;//關閉圖示
@BindView(R.id.lay_search)
RelativeLayout laySearch;//搜索布局
定義一個控制變數
private AutoTransition autoTransition;//過渡影片
private Animation bigShowAnim;//放大顯示
private Animation smallHideAnim;//縮小隱藏
private int width;//螢屏寬度
private boolean isOpen = false;//頂部搜索布局的狀態
然后在點擊方法中增加需求點擊的控制元件id

因為展開之后是不能設定固定的寬度,所以需要獲取螢屏的寬度,在initView方法中寫入
//獲取螢屏寬高
WindowManager manager = getWindowManager();
DisplayMetrics metrics = new DisplayMetrics();
manager.getDefaultDisplay().getMetrics(metrics);
width = metrics.widthPixels; //獲取螢屏的寬度 像素
而螢屏的寬度是px又需要轉換為dip,所以要寫轉換的方法;
// dp 轉成 px
private int dip2px(float dpVale) {
final float scale = getResources().getDisplayMetrics().density;
return (int) (dpVale * scale + 0.5f);
}
// px 轉成 dp
private int px2dip(float pxValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (pxValue / scale + 0.5f);
}
然后寫一個過渡影片的方法,會在展開和收縮的方法中呼叫
//過渡影片
@TargetApi(Build.VERSION_CODES.KITKAT)
private void beginDelayedTransition(ViewGroup view) {
autoTransition = new AutoTransition();
autoTransition.setDuration(500);
TransitionManager.beginDelayedTransition(view,autoTransition);
}
現在可以來寫具體的關于點擊展開和收縮的方法了,
/**
* 展開
*/
public void initExpand() {
isOpen = true;
edSearch.setVisibility(View.VISIBLE);//顯示輸入框
ivClose.setVisibility(View.VISIBLE);//顯示關閉按鈕
LinearLayout.LayoutParams LayoutParams = (LinearLayout.LayoutParams) laySearch.getLayoutParams();
LayoutParams.width = dip2px(px2dip(width) - 24);//設定展開的寬度
LayoutParams.setMargins(dip2px(0), dip2px(0), dip2px(0), dip2px(0));
laySearch.setPadding(14, 0, 14, 0);
laySearch.setLayoutParams(LayoutParams);
//開始影片
beginDelayedTransition(laySearch);
if (markerLatitude != 0) {//手動定位時
btnAutoLocation.hide();//隱藏自動定位按鈕
}
}
??這里做一下簡單的說明LayoutParams.width = dip2px(px2dip(width) - 24);//設定展開的寬度這里我先將螢屏的寬由px轉dp,然后剪去24,24就是螢屏左右各12的邊距,然后再轉成px賦值給LayoutParams.width,這樣LayoutParams就知道我這個控制元件到時候要展開多大了,而我在開始影片的時候也加了一個對于定位按鈕的判斷,因為這個控制元件和定位按鈕在同一水平線上,又因為底層的布局用的是FrameLayout,所以會出現覆寫的情況,這并不是我想要的,所以我加了一個控制,如果展開的時候處于手動定位則隱藏自動定位按鈕,當然我也在收縮的方法里面做了相應的處理,下面來看收縮的方法,
/**
* 收縮
*/
private void initClose() {
isOpen = false;
edSearch.setVisibility(View.GONE);
edSearch.setText("");
ivClose.setVisibility(View.GONE);
LinearLayout.LayoutParams LayoutParams = (LinearLayout.LayoutParams) laySearch.getLayoutParams();
LayoutParams.width = dip2px(48);
LayoutParams.height = dip2px(48);
LayoutParams.setMargins(dip2px(0), dip2px(0), dip2px(0), dip2px(0));
laySearch.setLayoutParams(LayoutParams);
//隱藏鍵盤
InputMethodManager inputMethodManager = (InputMethodManager) this.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(this.getWindow().getDecorView().getWindowToken(), 0);
//開始影片
beginDelayedTransition(laySearch);
if (markerLatitude != 0) {//自動定位
btnAutoLocation.show();//隱藏自動定位按鈕
}
}
當然我們需要在點擊的時候呼叫這兩個方法

收縮的方法比較的簡單一些,加了收縮時關閉鍵盤的動作,下面演示一下

重點注意看上邊的效果,下面要對輸入框做一下簡單的控制,
在initView中增加
/**
* 輸入法鍵盤的搜索監聽
*/
edSearch.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
String city = edSearch.getText().toString();
if (!TextUtils.isEmpty(city)) {
//得到輸入的內容
} else {
ToastUtils.showShortToast(context, "請輸入城市名稱");
}
}
return false;
}
});
這里其實就是對輸入法簡單的回車按鈕的監聽,這也是現在很多app的通用做法,不需要再自己去寫一個搜索按鈕來控制,而使用輸入法的回車鍵作為搜索按鈕,那么是怎么改的呢?通過布局

??相信你已經知道了,好的,我在點擊搜索的時候獲取輸入框的內容,為空則提示一下,不為空就獲取內容,那么獲取到的內容要怎么辦呢?首先想一下獲取到的是什么內容?當然是城市的名稱了,可以是區/縣、市,那么得到這個資料就去直接通過搜索城市拿到城市id,再通過城市id去請求天氣資料嗎?這樣是可以的,但是忽略了地圖,你不要忘記了,這個頁面是地圖天氣,所以要和地圖有聯動才行啊,回想一下之前我通過定位獲取到坐標,通過坐標拿到了城市的具體資訊,那么反過來通過地址資訊,拿到坐標,再把坐標渲染在地圖上,通過定位到這個坐標,聽起來是不是覺得比較難?實際上真的難嗎?你只要想清楚邏輯,剩下的就是實作而已了,下面來看看怎么實作的吧,

通過這個做決議,不光是坐標轉地址,也可以地址轉坐標

在輸入法搜索按鈕點擊后,獲取到的內容不為空則進行地址的決議,new一個GeoCodeOpting(),傳入城市和地址,這里可以傳同樣的值,那么決議的結果呢?

??要知道我之前就給這個編碼結果做了監聽,當時只在onGetReverseGeoCodeResult中做了處理,因為這個回傳是負責坐標轉地址的,而onGetGeoCodeResult是負責地址轉坐標的,很好,這正是我想要的,我在回傳值中做了一些簡單的處理,如果決議不到資料我們就認定你輸入的城市名有問題,提示你一下并清空這個輸入框讓你重新輸入,如果輸入的內容沒啥問題,我們就獲取坐標,然后列印出地址和經緯度,你可以自己運行試一下絕對就是這樣的,OK,既然現在拿到了坐標那就可以定位了對不對,先來看看之前的地圖上手動定位繪制標點的代碼

這里你還記得嗎?在點擊地圖是重新繪制,然后標點,最后重新定位的,那么你同樣可以把這一段代碼復制過去那邊,就可以了,不過為了不寫重新代碼,可以寫一個方法兩個地方使用,反正你只要傳入一個坐標的物件就可以了,不是嗎?方法如下:
/**
* 重新定位
*
* @param latLng 坐標
*/
private void resetLocation(LatLng latLng) {
bitmap = BitmapDescriptorFactory.fromResource(R.mipmap.icon_marka);// 設定marker圖示
//通過LatLng獲取經緯度
markerLatitude = latLng.latitude;//獲取緯度
markerLongitude = latLng.longitude;//獲取經度
mBaiduMap.clear();//清除之前的圖層
MarkerOptions options = new MarkerOptions()//創建標點marker設定物件
.position(latLng)//設定標點的定位
.icon(bitmap);//設定標點圖示
marker = (Marker) mBaiduMap.addOverlay(options);//在地圖上顯示標點
//重新定位
initLocation();
}
呼叫

然后再回到之前通過位置獲取到坐標的那個回傳方法里呼叫即可

這里我還多加了一個關閉搜索布局的方法代碼,來運行一下吧,

你以為這就完了嗎?當然沒有!我真是猜不透我自己啊!哈哈哈哈!
OK,我們還需要與這個底部控制元件做協調,比如我們之前有過這樣一個操作就是當手動定位時,拖動底部布局到頂部然后隱藏這個按鈕,回到底部時顯示這個按鈕,那么同理我是不是也應該對這個搜索布局做同樣的事呢?當然了,當然了,褲衩著火,襠燃了,最近相聲聽得比較多,學到了這么一句俏皮話,
好的,繼續往下看啊,因為我用的不是浮動按鈕,所以就沒有默認的影片了,那么就需要自己來寫影片效果,這個其實也不難啊,

在mvplibrary中的anim包下新建兩個影片xml檔案
scale_big_expand.xml
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromXScale="0"
android:fromYScale="0"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="1"
android:toYScale="1" />
scale_small_close.xml
<?xml version="1.0" encoding="utf-8"?>
<scale xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="200"
android:fromXScale="1"
android:fromYScale="1"
android:pivotX="50%"
android:pivotY="50%"
android:toXScale="0"
android:toYScale="0" />
然后回到MapWeatherActivity,在initView中增加
//放大
bigShowAnim = AnimationUtils.loadAnimation(context, R.anim.scale_big_expand);
//縮小
smallHideAnim = AnimationUtils.loadAnimation(context, R.anim.scale_small_close);
可以看到這里加了影片的xml,下面就要寫一個方法用于控制顯示和隱藏分別呼叫不用的影片,方法如下:
/**
* 縮放影片
* @param view 需要縮放的控制元件
* @param state 狀態 顯示或者隱藏
*/
private void scaleAnimation(View view,String state) {
switch (state){
case "show":
view.startAnimation(bigShowAnim);
view.setVisibility(View.VISIBLE);
break;
case "hide":
view.startAnimation(smallHideAnim);
smallHideAnim.setAnimationListener(new Animation.AnimationListener() {
@Override
public void onAnimationStart(Animation animation) {
}
@Override
public void onAnimationEnd(Animation animation) {
view.setVisibility(View.GONE);
}
@Override
public void onAnimationRepeat(Animation animation) {
}
});
break;
}
}
方法比較的簡單,相信都不用我明說了,下面來看在哪里呼叫這個方法呢?當然是在底部布局拖動的時候啊

收縮

展開

展開這里稍微講解一下,因為展開的時候我并不知道你的搜索布局是否展開,所以加了一個判斷,如果你的搜索布局是展開的,那么先收縮的搜索布局,再隱藏的搜索布局,這里新開了一個延時執行緒,500毫秒后執行隱藏影片,為什么是500毫秒呢?因為搜索布局收縮的過渡影片設定的時間就是500毫秒,這樣就能做到無縫連接了,有沒有恍然不明白的感覺啊?同樣,如果底部布局展開時,搜索布局沒有展開則直接隱藏即可,那么再來運行一下看看效果如何?

其實到這里文章就已經完畢了,然而還差一丟丟,再把每日描述給優化一下吧,打開WeatherUtil,修改uvIndexInfo方法
/**
* 紫外線等級描述
* @param uvIndex
* @return
*/
public static String uvIndexInfo(String uvIndex) {
String result = null;
Log.d("uvIndex-->", uvIndex);
int level = Integer.parseInt(uvIndex);
if (level <= 2) {
result = "較弱";
} else if (level <= 5) {
result = "弱";
} else if (level <= 7) {
result = "中等";
} else if (level <= 10) {
result = "強";
} else if (level <= 15) {
result = "很強";
}
return result;
}
之前我根據和風的檔案寫只有1~5,結果發現有一次出現了11,當然我就意識到和風坑了我,所以我去百度了紫外線的等級劃分,于是就有了上面的代碼,同樣也衍生出另一個方法
/**
* 紫外線詳細描述
* @param uvIndexInfo
* @return
*/
public static String uvIndexToTip(String uvIndexInfo) {
String result = null;
switch (uvIndexInfo) {
case "較弱":
result = "紫外線較弱,不需要采取防護措施;若長期在戶外,建議涂擦SPF在8-12之間的防曬護膚品,";
break;
case "弱":
result = "紫外線弱,可以適當采取一些防護措施,涂擦SPF在12-15之間、PA+的防曬護膚品,";
break;
case "中等":
result = "紫外線中等,外出時戴好遮陽帽、太陽鏡和太陽傘等;涂擦SPF高于15、PA+的防曬護膚品,";
break;
case "強":
result = "紫外線較強,避免在10點至14點暴露于日光下.外出時戴好遮陽帽、太陽鏡和太陽傘等,涂擦SPF20左右、PA++的防曬護膚品,";
break;
case "很強":
result = "紫外線很強,盡可能不在室外活動,必須外出時,要采取各種有效的防護措施,";
break;
}
return result;
}
這個方法就體現出紫外線的強度的效果描述,然后回到MapWeatherActivity中
找到getAirNowResult
tvTodayInfo.setText("今天,白天" + dayInfo + ",晚上" + nightInfo +
",現在" + tvTemperature.getText().toString() + "," +
WeatherUtil.apiToTip(data.getCategory()) + WeatherUtil.uvIndexToTip(tvUvIndex.getText().toString()));
修改這一行代碼,然后就OK了,運行一下:

然后這一部分的代碼就算是寫完了,雖然看起來功能不多,但是自己來寫的話卻沒有那么容易啊,
原始碼如下:
Good Weather 歡迎Star或者Fork
聯系郵箱 [email protected]
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/1298.html
標籤:其他
