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

Android自動更新:這里的更新靜悄悄~
產(chǎn)品:APP的底部按鈕能夠做到自動更新嗎?

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

產(chǎn)品:那京東、美團(tuán)是怎么做到的?

累人猿:……

心疼自己十分鐘~

經(jīng)??吹接惺裁椿顒拥臅r候,京東、淘寶、美團(tuán)這些應(yīng)用都不需要更新應(yīng)用就能夠?qū)崿F(xiàn)應(yīng)用UI的更新,特別是底部菜單按鈕的圖標(biāo),及時的營造出活動的氣氛。作為一個移動開發(fā)者都想弄明白他們是怎么做到的,我所了解的方式有以下三種:

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

2、純原生,這種方式是通過圖片下載,本地圖片讀取,動態(tài)生成StateListDrawable,大致這樣一個流程實現(xiàn)不更新應(yīng)用就更新底部按鈕的圖標(biāo);

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

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

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

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

這里的排版沒有弄好,小伙伴們可以到這里去看這篇文章:Android自動更新:這里的更新靜悄悄~

一、CSS布局

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

<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>

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

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%,如果按鈕的個數(shù)修改一定要注意這個百分比的修改。

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

/*禁止長按復(fù)制功能

*/*{

-webkit-touch-callout:none;

-webkit-user-select:none;

-khtml-user-select:none;

-moz-user-select:none;

-ms-user-select:none;

user-select:none;

}

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

二、JS交互

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

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

/*

js調(diào)用本地的方法改變底部按鈕的選中狀態(tài)原理:js調(diào)用本地方法,方法內(nèi)部通過廣播的方式傳遞選中按鈕的編號

*/@JavascriptInterface

public voidchangeTab(String index) {

Intent intent =newIntent();

intent.setAction("ChangeTab");

intent.putExtra("index", index);

context.sendBroadcast(intent);

}

/*將Json數(shù)組傳遞給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調(diào)用本地的方法實現(xiàn)原生fragment的切換,我使用了廣播的方式,來通知原生界面的切換操作。

第二個方法,是為了初始化h5界面,通過js調(diào)用這個方法,將數(shù)據(jù)傳遞給h5頁面,達(dá)到初始化界面的效果。

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

public classItem {

privateStringicon_nor;//圖標(biāo)正常狀態(tài)privateStringicon_sel;//圖標(biāo)選中狀態(tài)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;

}

}

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

不如我們先來看看應(yīng)用截圖吧!


運行截屏

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


服務(wù)器關(guān)閉截圖

這種情況是服務(wù)器關(guān)閉的情況,看著并沒有什么區(qū)別,因為我做了容錯處理,其實這種情況圖片是讀取失敗的,如果沒有容錯處理,圖片會顯示一個錯誤圖片的圖標(biāo)。
圖片地址丟失截圖

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

繼續(xù)回到源碼分析,在點擊某個tab的時候,相應(yīng)ui也應(yīng)該發(fā)生變化,還記得剛剛我們在原生提供的第一個方法嗎,現(xiàn)在就是使用它的時候,

/*通過js調(diào)用本地方法,改變tab選中狀態(tài)

*/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);

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

/*初始化數(shù)據(jù),為了排除網(wǎng)絡(luò)等不確定因素,需要在images文件夾下面放一套缺省圖標(biāo)

*/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;

}

}

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


目錄結(jié)構(gòu)

建議使用的小伙伴不要把你們的圖片改成截圖中的名字,這樣就不需要再html源碼中修改。
然后就是處理某個tab選中之后的樣式修改了。

/*改變選中按鈕的樣式和狀態(tài)

*/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的方法。

/*提供給原生的方法,原生調(diào)用該方法,實現(xiàn)設(shè)置某個tab選中

*/functionsetChecked(id) {

varbtn=document.getElementById(id);

btn.checked=true;

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

appNative.changeTab(id);

myFun("img_"+ id);

}

}

使用的時候只需要原生中調(diào)用,方法如下:

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

其中的”four”對應(yīng)某個需要跳轉(zhuǎn)到的tab編號,注意只能是“one”、“two”、“three”、“four”

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

三、原生調(diào)用

講了CSS和JS,接著應(yīng)該講講怎么在原生中調(diào)用。

首先加載整個tab的html頁面

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

當(dāng)然初始化數(shù)據(jù)是少不了的

/*將圖片地址和按鈕文案傳遞給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將數(shù)據(jù)傳遞給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這個方法里面調(diào)用js方法,這樣不容易出錯。

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

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

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容