Android自動更新:這里的更新靜悄悄~

Android自動更新:這里的更新靜悄悄~

(第一篇文章沒有把樣式弄好,所以重新弄了一遍,歡迎小伙伴讀這篇文章:Android自動更新:這里的更新靜悄悄~ - 簡書)

產品:APP的底部按鈕能夠做到自動更新嗎?

累人猿:有些麻煩(無辜臉)~

產品:那京東、美團是怎么做到的?

累人猿:……

心疼自己十分鐘~

經常看到有什么活動的時候,京東、淘寶、美團這些應用都不需要更新應用就能夠實現應用UI的更新,特別是底部菜單按鈕的圖標,及時的營造出活動的氣氛。作為一個移動開發者都想弄明白他們是怎么做到的,我所了解的方式有以下三種:

1、使用前端框架,如果是這種情況的話,整個應用都沒有多少原生的東西,比較火的框架:React Native、ionic等;

2、純原生,這種方式是通過圖片下載,本地圖片讀取,動態生成StateListDrawable,大致這樣一個流程實現不更新應用就更新底部按鈕的圖標;

3、JS交互:將整個底部按鈕做成H5,然后使用webview加載,這也是今天的重點。

其中第二和第三種方式,必須得做好容錯處理,比如網絡問題導致圖片下載失敗,本地圖片丟失等問題,為了避免這些問題導致應用的不正常運行,我們需要有備選方案,一旦問題出現,就放棄整個流程,從本地讀取資源。

我并不是前端開發者,h5相關的東西學得不多,所以在實現這個功能的過程中還是花了一些功夫的,JS和CSS用得也不熟,所以在處理有些細節的時候,用得方式有些暴力,希望大伙兒能夠理解,有考慮使用這種方式的小伙伴可以讓h5開發的小伙伴來做。

整個功能是基于JS交互這個基礎上的,至于需要怎么交互,我就不做過多的講解了,不清楚的小伙伴可以去網上找一些資料來學學。

這里的排版沒有弄好,小伙伴們可以到這里去看這篇文章:blog.csdn.net/zhimingshangyan/article/details/52767939

一、CSS布局

整個底部按鈕的界面,都是一些html標簽,我使用了比較經典的四個按鈕,如果有特別需求的小伙伴,可以自己增加或者刪除幾個標簽,源碼如下:

<div>

<label?onclick="myFun('img_one')">

<input ?type="radio"?id="one"?name="tabBtn"?checked>

<img?id="img_one">

<p?style="color: #f00;"></p>

</label>

<label?onclick="myFun('img_two')">

<input?type="radio"?id="two"?name="tabBtn">

<img?id="img_two">

<p></p>

</label>

<label?onclick="myFun('img_three')">

<input?type="radio"id="three"name="tabBtn">

<img?id="img_three">

<p></p>

</label>

<label?onclick="myFun('img_four')">

<input?type="radio"id="four"name="tabBtn">

<img?id="img_four">

<p></p>

</label>

</div>

當然,要實現整個界面方式不止一種,我采用的是radio的方式,接著我們講講css中比較關鍵的地方,第一label的p標簽設置了文字顏色為紅色,是為了初始化,一般應進入應用第一個按鈕是選中狀態,當然這個工作可以放在JS中來做,和圖片初始化一起做,如果設計不是紅色的小伙伴就要注意了,在使用的時候需要修改成設計要求的顏色。

label的CSS代碼:

label{

margin-top:0px;

margin-bottom: -3px;

padding-top:5px;

display:inline-block;

width:25%;

font-weight:normal;

vertical-align:middle;

cursor:pointer;

float:left;

text-align:center;

}

一定要注意25%,因為是四個按鈕,我們需要平分100%,所以每個按鈕的范圍是25%,如果按鈕的個數修改一定要注意這個百分比的修改。

因為使用的webview加載h5的方式,細心的小伙伴會發現,我長按某個按鈕的時候可以復制文本的,所以為了禁用復制功能,我們需要在CSS中做一些限制

/*禁止長按復制功能

*/*{

-webkit-touch-callout:none;

-webkit-user-select:none;

-khtml-user-select:none;

-moz-user-select:none;

-ms-user-select:none;

user-select:none;

}

其實就做了一件事,需要這么些代碼是因為兼容不懂瀏覽器內核。關于div、p、body、input的CSS代碼這里就不詳細講解了,這幾個標簽的CSS代碼可以不做任何修改,直接使用,如果要改的話可以讓h5的小伙伴幫忙改得更規范一些。

二、JS交互

不管是ios還是android都提供了Webview和h5交互的方法。

我們先來看看本地提供給JS的接口方法:

/*

js調用本地的方法改變底部按鈕的選中狀態原理:js調用本地方法,方法內部通過廣播的方式傳遞選中按鈕的編號

*/@JavascriptInterface

public voidchangeTab(String index) {

Intent intent =newIntent();

intent.setAction("ChangeTab");

intent.putExtra("index", index);

context.sendBroadcast(intent);

}

/*Json數組傳遞給h5頁面

*/@JavascriptInterface

publicString getImages(List lists) {

try{

JSONArray array =newJSONArray();

for(Item item : lists) {

JSONObject object =newJSONObject();

object.put("icon_nor", item.getIcon_nor());

object.put("icon_sel", item.getIcon_sel());

object.put("title", item.getTitle());

array.put(object);

}

String json = array.toString();

returnjson;

}catch(Exception e) {

e.printStackTrace();

}

return"";

}

第一個方法的作用是,在h5界面點擊某個按鈕的使用,js調用本地的方法實現原生fragment的切換,我使用了廣播的方式,來通知原生界面的切換操作。

第二個方法,是為了初始化h5界面,通過js調用這個方法,將數據傳遞給h5頁面,達到初始化界面的效果。

其中Item是我將每個按鈕封裝成了一個對象,這樣方便數據的讀取和傳遞,源碼如下:

public classItem {

privateStringicon_nor;//圖標正常狀態privateStringicon_sel;//圖標選中狀態privateStringtitle;//按鈕文案publicString getIcon_nor() {

returnicon_nor;

}

public voidsetIcon_nor(String icon_nor) {

this.icon_nor= icon_nor;

}

publicString getIcon_sel() {

returnicon_sel;

}

public voidsetIcon_sel(String icon_sel) {

this.icon_sel= icon_sel;

}

publicString getTitle() {

returntitle;

}

public voidsetTitle(String title) {

this.title= title;

}

}

其實這個對象中還應該增加一個顏色變量,這樣的話,我們不需要修改h5的顏色,直接通過傳值來改變顏色,有興趣的小伙伴可以嘗試一下,一定要記得在Js中獲取顏色和應用顏色。

不如我們先來看看應用截圖吧!

運行錄屏

這是最正常的情況,也就是說,服務器正常開啟,圖片地址正確傳遞。

服務器關閉截圖

這種情況是服務器關閉的情況,看著并沒有什么區別,因為我做了容錯處理,其實這種情況圖片是讀取失敗的,如果沒有容錯處理,圖片會顯示一個錯誤圖片的圖標。

圖片地址丟失截圖

這種情況是,我故意少傳了一張圖片的運行界面,細心的小伙伴會發現這張圖中的tab后面的編號跟前面一張的圖片不一樣,其實這也算是一種保底的做法,一旦出錯,就放棄方法,采用本地的不就方案,保證應用的運行沒有問題。

繼續回到源碼分析,在點擊某個tab的時候,相應ui也應該發生變化,還記得剛剛我們在原生提供的第一個方法嗎,現在就是使用它的時候,

/*通過js調用本地方法,改變tab選中狀態

*/varoneRB=document.getElementById("one");

oneRB.addEventListener('click',function() {

if(oneRB.checked) {

appNative.changeTab('one');

}

},false);

vartwoRB=document.getElementById("two");

twoRB.addEventListener('click',function() {

if(twoRB.checked) {

appNative.changeTab('two');

}

},false);

varthreeRB=document.getElementById("three");

threeRB.addEventListener('click',function() {

if(threeRB.checked) {

appNative.changeTab('three');

}

},false);

varfourRB=document.getElementById("four");

fourRB.addEventListener('click',function() {

if(fourRB.checked) {

appNative.changeTab('four');

}

},false);

啟動應用的時候,我們需要初始化整個tab界面,第二個方法排上用場了,

/*初始化數據,為了排除網絡等不確定因素,需要在images文件夾下面放一套缺省圖標

*/vardatas;

varisOk=true;

varicons=newArray();

varicon1=newObject();

icon1.icon_sel="images/icon_tab_one_sel.png";

icon1.icon_nor="images/icon_tab_one_nor.png";

icon1.title="tab0";

icons.push(icon1);

varicon2=newObject();

icon2.icon_sel="images/icon_tab_two_sel.png";

icon2.icon_nor="images/icon_tab_two_nor.png";

icon2.title="tab1";

icons.push(icon2);

varicon3=newObject();

icon3.icon_sel="images/icon_tab_three_sel.png";

icon3.icon_nor="images/icon_tab_three_nor.png";

icon3.title="tab2";

icons.push(icon3);

varicon4=newObject();

icon4.icon_sel="images/icon_tab_four_sel.png";

icon4.icon_nor="images/icon_tab_four_nor.png";

icon4.title="tab3";

icons.push(icon4);

varoImg=document.getElementsByTagName('img');

functiontranData(jsondata) {

datas=eval(jsondata);

if(isEmpty(jsondata)){

isOk=false;

}else{

for(i=0;i<datas.length;i++){

if(typeofdatas[i].icon_sel==="undefined"||typeofdatas[i].icon_nor==="undefined"){

isOk=false;

}

}

}

if(isOk){

oImg[0].src=datas[0].icon_sel;

oImg[0].nextSibling.nextSibling.innerText=datas[0].title;

oImg[0].onerror=function(){

oImg[0].src=icons[0].icon_sel;

isOk=false;

}

oImg[1].src=datas[1].icon_nor;

oImg[1].nextSibling.nextSibling.innerText=datas[1].title;

oImg[1].onerror=function(){

oImg[1].src=icons[1].icon_nor;

isOk=false;

}

oImg[2].src=datas[2].icon_nor;

oImg[2].nextSibling.nextSibling.innerText=datas[2].title;

oImg[2].onerror=function(){

oImg[2].src=icons[2].icon_nor;

isOk=false;

}

oImg[3].src=datas[3].icon_nor;

oImg[3].nextSibling.nextSibling.innerText=datas[3].title;

oImg[3].onerror=function(){

oImg[3].src=icons[3].icon_nor;

isOk=false;

}

}else{

oImg[0].src=icons[0].icon_sel;

oImg[0].nextSibling.nextSibling.innerText=icons[0].title;

oImg[1].src=icons[1].icon_nor;

oImg[1].nextSibling.nextSibling.innerText=icons[1].title;

oImg[2].src=icons[2].icon_nor;

oImg[2].nextSibling.nextSibling.innerText=icons[2].title;

oImg[3].src=icons[3].icon_nor;

oImg[3].nextSibling.nextSibling.innerText=icons[3].title;

}

}

這段處理有些復雜,因為需要考慮容錯方法,所以我建議小伙伴們,把整個html放在app的assets文件夾下面,不要放在服務端,同時我們還要再assets文件夾下面放一套默認圖標,我個人認為圖標更新失敗,比應用不能夠正常使用要有好的多,建議采用如下的方式,使用這種方案,主要的思路就是不管什么原因導致遠程圖片讀取失敗,都直接使用本地的缺省圖片,還要注意,是整套圖片都使用本地的,不能夠某一張讀取失敗了,只替換某一張噻~

目錄結構

建議使用的小伙伴不要把你們的圖片改成截圖中的名字,這樣就不需要再html源碼中修改。

然后就是處理某個tab選中之后的樣式修改了。

/*改變選中按鈕的樣式和狀態

*/functionmyFun(sId) {

for(vari=0;i<oImg.length;i++) {

iconSelect(i,sId);

}

}

functioniconSelect(i, sId){

if(oImg[i].id== sId) {

oImg[i].previousSibling.previousSibling.checked=true;

oImg[i].nextSibling.nextSibling.style.color="red";

if(isOk){

oImg[i].src=datas[i].icon_sel;

}else{

oImg[i].src=icons[i].icon_sel;

}

}else{

if(isOk){

oImg[i].src=datas[i].icon_nor;

}else{

oImg[i].src=icons[i].icon_nor;

}

oImg[i].nextSibling.nextSibling.style.color="black";

}

}

另外,有時候我們會遇到在某個具體的fragment中點擊某個按鈕跳到其它tab頁面中去,比如在fragment中我點擊了按鈕,需要跳到第二個tab頁面,所以我通過js提供了一個切換tab的方法。

/*提供給原生的方法,原生調用該方法,實現設置某個tab選中

*/functionsetChecked(id) {

varbtn=document.getElementById(id);

btn.checked=true;

if(typeofappNative !=="undefined"&& appNative.changeTab) {

appNative.changeTab(id);

myFun("img_"+ id);

}

}

使用的時候只需要原生中調用,方法如下:

webView.loadUrl("javascript:setChecked('four')");

其中的”four”對應某個需要跳轉到的tab編號,注意只能是“one”、“two”、“three”、“four”

前面就是整個功能的js代碼,我知道,有些功能是完全可以用CSS來實現的,CSS我還不是很熟,所以暴力的使用了JS來處理細節,并且我并沒有使用JQuery這些簡單的語法結構,沒有別的原因,我還不會~

三、原生調用

講了CSS和JS,接著應該講講怎么在原生中調用。

首先加載整個tab的html頁面

webView.loadUrl("file:///android_asset/radioGroup.html");

當然初始化數據是少不了的

/*將圖片地址和按鈕文案傳遞給h5頁面

*/private voidinitData() {

lists=newArrayList<>();

Item item =newItem();

item.setIcon_nor("http://192.168.111.20/drawable-hdpi/icon_tab_one_nor.png");

item.setIcon_sel("http://192.168.111.20/drawable-hdpi/icon_tab_one_sel.png");

//item.setIcon_sel("http://192.168.111.20/drawable-hdpi/push.png");item.setTitle("tab1");

Item item1 =newItem();

item1.setIcon_nor("http://192.168.111.20/drawable-hdpi/icon_tab_two_nor.png");

item1.setIcon_sel("http://192.168.111.20/drawable-hdpi/icon_tab_two_sel.png");

item1.setTitle("tab2");

Item item2 =newItem();

item2.setIcon_nor("http://192.168.111.20/drawable-hdpi/icon_tab_three_nor.png");

item2.setIcon_sel("http://192.168.111.20/drawable-hdpi/icon_tab_three_sel.png");

item2.setTitle("tab3");

Item item3 =newItem();

item3.setIcon_nor("http://192.168.111.20/drawable-hdpi/icon_tab_four_nor.png");

item3.setIcon_sel("http://192.168.111.20/drawable-hdpi/icon_tab_four_sel.png");

item3.setTitle("tab4");

lists.add(item);

lists.add(item1);

lists.add(item2);

lists.add(item3);

}

然后需要通過js將數據傳遞給html頁面

webView.setWebViewClient(newWebViewClient() {

@Override

public booleanshouldOverrideUrlLoading(WebView view, String url) {

return super.shouldOverrideUrlLoading(view, url);

}

@Override

public voidonLoadResource(WebView view, String url) {

super.onLoadResource(view, url);

}

@Override

public voidonPageFinished(WebView view, String url) {

super.onPageFinished(view, url);

webView.loadUrl("javascript:tranData("+javaScriptInterface.getImages(lists) +")");

//webView.loadUrl("javascript:setChecked('four')");}

});

建議在onPageFinished這個方法里面調用js方法,這樣不容易出錯。

至于原生是怎么處理tab頁面的切換的,我就不講了,有興趣的小伙伴可以把源碼下載下來看一看,當然自己改改是最好的方式。

傳送門開啟:http://download.csdn.net/detail/zhimingshangyan/9648669

我的實現思路講完了,文章的開篇我提供了三種實現方式,第二和第三兩種方式我都實現了,當然第二種方式我沒有整理,歡迎小伙伴提供你們的思路給我,同時有什么問題和值得改進的地方小伙伴可以留言給我。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容