一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Android - Android UI實時預覽和編寫的各種技巧

Android UI實時預覽和編寫的各種技巧

2021-04-12 10:33Android開發網 Android

大家好,今天給大家分享的是Android中實時預覽UI和編寫UI的各種技巧,

一、啰嗦

之前有讀者反饋說,你搞這個所謂的最佳實踐,每篇文章最后就給了一個庫,感覺不是很高大上。其實,我在寫這個系列之初就有想過這個問題。我的目的是:給出最實用的庫來幫助我們開發,并且盡可能地說明這個庫是如何編寫的,希望讓初創公司的程序員少寫點給后人留坑的代碼(想必大家對此深有體會)。

我之前給出的庫都是很簡單基礎的,基本是一看就懂(但足夠精妙),如果以后的文章涉及到了復雜的庫,我會專門附加一篇庫的講解文。

如果一個庫的原理你知道,此外這個庫很容易擴展和維護,而且它還用到了很多最佳實踐的經驗,你為什么不去試試呢?程序的意義在于把前人的優秀思維和豐富經驗記錄下來,讓使用者可以輕易地站在巨人的肩膀上。它的意義甚至堪比于將祖先的智慧通過dna遺傳給我們,它是一種顛覆性的存在。如果我僅僅是分享自己在實踐中獲得的很多經驗,這就不是程序,而是教育!
令人遺憾的是,我只能將很多有章可循的東西包裝為庫,而調試ui這種雜亂無章的技巧只能通過文章來記錄,故產生了此文。

二、需求

有很多初學者都聽到前輩們說android studio(下文簡稱為as)的布局實時預覽很強大,但是當我們真正使用as后就會發現很多界面在預覽時是這樣的:

Android UI實時預覽和編寫的各種技巧

或者是這樣的

Android UI實時預覽和編寫的各種技巧

甚至是這樣的:

Android UI實時預覽和編寫的各種技巧

這時候誰再和我講as可以讓你實時地編寫ui,我就要和誰拼命了。(┬_┬)
其實這個不是as的錯,而是開發者(包括google的開發人員)的錯。因為很多開發者不注重實時的ui顯示,一切都是以真機運行的結果做評判標準,從而產生了很多無法預覽,但能運行的界面。在很多項目中,一個原本可以一秒內看到的效果,最終需要漫長的過程(編譯->運行->安裝->顯示)才能被我們看到。我不得不說這是反人類的,大大降低了android程序員的開發效率,破壞了開發的心情(我是很注重開發心情的),讓as強大的預覽功能變得形同虛設。那么,既然官方不作為,只有我們自己來!下面就來說說如何讓自己的ui可實時調試的方案和技巧。

三、原則與技巧

3.0 指導性原則

將一次性的屬性放入xml中,將需要根據程序運行產生變化的屬性放入java代碼中。

得益于布局文件的可預覽性(即使某個控件不可預覽,我們也應該讓其支持預覽,下文會給出方案),我們可以大膽的編寫xml布局,而不用擔心后期維護難以定位的問題。僅將動態變化的東西放入java代碼中,就可以讓可變和不可變的代碼進行分離,從而在本質上趨于設計模式原則,在以后的編寫過程中你將會發現代碼自動產生了很多優化的空間,可讀性也增強了很多。

3.1 少用merge標簽

很多文章都說為了避免層級加深請用merge標簽,但是我這里卻說少用它。原因有兩點: 1. merge標簽會讓布局中各個元素的關系錯亂,無法準確的顯示ui位置(預覽時)。 2. 在merge標簽中會失去as自動的代碼提示功能,讓編寫變得困難。
這兩點對于ui的實時預覽是極為致命的,所以推薦先用linearlayout等viewgroup做根布局,等編寫完畢了后再用merge來代替。我倒不是說merge標簽不好,merge標簽的設計思路是很棒的,我只是想指出其問題。可惜的是,這兩個問題目前沒什么其他的好的解決方案了,只能等官方改進ide和增加tools的功能吧。

【吐槽】

一個很棒的merge標簽被這兩個因素弄的很別扭,真是令人傷心,和它同病相憐的還有tools這個命名空間。

3.2 多用tools的屬性

xmlns:tools="http://schemas.android.com/tools"是一個很重要也是很好用的命名空間,它擁有android:中所有的屬性,但它標識的屬性僅僅在預覽中有效,不會影響真正的運行結果。

舉個例子:

?
1
2
3
4
5
<textview
  android:text="footer"
  android:layout_width="wrap_content"
  android:layout_height="100dp"
  />

這是我們之前的一個寫法,把textview的text屬性用android:來標識。如果我們希望這個textview的文字在代碼中實時控制,默認是沒文字怎么辦?這就需要tools的幫助了。

?
1
2
3
4
5
<textview
    tools:text="footer"
    android:layout_width="wrap_content"
    android:layout_height="100dp"
    />

把第一行的android替換為tools這樣既可以能在預覽中看到效果,又不會影響代碼實際運行的結果。因為在實際運行的時候被tools標記的屬性是會被忽略的。你完全可以理解為它是一個測試環境,這個測試環境和真實環境是完全獨立的,不會有任何影響。

【吐槽】

tools標簽不支持代碼提示,而且自己的屬性也不能提示,全是靠自己記憶,或者先用android來代替,然后替換android為tools。這么長時間以來,google貌似一直沒管它,這也印證了google程序員也是不怎么愛實時預覽布局的人。

3.3 用tools來讓listview支持實時預覽

在之前的代碼中,我們總是這樣寫listview,然后腦補一下item放入的樣子。

?
1
2
3
4
5
<?xml version="1.0" encoding="utf-8"?>
<listview xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  />

Android UI實時預覽和編寫的各種技巧

現在我們可以利用tools來預覽item被放入的樣子了,就像這樣:

?
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="utf-8"?>
<listview xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  tools:listheader="@layout/demo_header"
  tools:listitem="@layout/demo_item"
  />

Android UI實時預覽和編寫的各種技巧

是不是好了很多呢。

利用tools的這兩個屬性可以讓我們不用盲寫ui了,也可以給設計一個很直觀的展示。

3.4 利用drawablexxx屬性來做有圖文的控件

textview和其子類都擁有drawableleft、drawableright等屬性,通過這些屬性可以讓我們很方便的做出有圖文控件。drawablepadding可以設置圖文之間的間距,但可惜沒有drawableleftpadding之類的屬性。 比如我們要做一個兩邊有icon,文字居中的控件:

Android UI實時預覽和編寫的各種技巧

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<textview xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="50dp"
  android:textappearance="?android:attr/textappearancelistitemsmall"
  android:gravity="center_vertical|center_horizontal"
  android:drawableleft="@drawable/demo_tab_home_selector"
  android:drawableright="@drawable/demo_tab_home_selector"
  android:drawablepadding="10dp"
  android:text="ddd"
  android:textsize="20sp"
  />

這時如果想調整文字位置,只需要修改gravity的值即可。

Android UI實時預覽和編寫的各種技巧

我們常見的這種(文字+箭頭)的控件就可以按照如下方式進行制作:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
<?xml version="1.0" encoding="utf-8"?>
<textview xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:tools="http://schemas.android.com/tools"
  android:layout_width="match_parent"
  android:layout_height="60dp"
  android:padding="16dp"
  android:textappearance="?android:attr/textappearancelistitemsmall"
  android:gravity="center_vertical"
  android:drawableright="@drawable/icon_arrow"
  android:drawablepadding="10dp"
  android:text="設置菜單"
  android:textsize="20sp"
  />

3.5 利用space和layout_weight做占位

有時候我們的需求很復雜,希望一個linearlayout中多個控件分散于兩邊,因為linearlayout內部的控件只能按照順序依次排列,想要完成這個效果要用到space了。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<?xml version="1.0" encoding="utf-8"?>
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="horizontal"
  android:gravity="center_vertical"
  android:padding="12dp"
  >
  <textview
    android:layout_width="wrap_content"
    android:layout_height="100dp"
    android:gravity="center"
    android:text="header"
    android:textsize="40sp"
    />
  <space
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_weight="1"
    />
  <imageview
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/tab_icon_home"
    />
</linearlayout>

Android UI實時預覽和編寫的各種技巧

再舉個常見的例子:

我們要做一個上面是viewpager,底部是tab欄的主頁面。這種頁面如果僅僅用linearlayout是沒辦法做的,但如果用了layout_weight就可以很方便的完成。

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android"
  xmlns:app="http://schemas.android.com/apk/res-auto"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  android:orientation="vertical"
  >
  <kale.uidemo.exviewpager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1.0"
    />
  <kale.uidemo.extablayout
    android:id="@+id/tablayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    />
</linearlayout>

關鍵代碼:

 

復制代碼 代碼如下:

    android:layout_height="0dp"
    android:layout_weight="1.0"

 

Android UI實時預覽和編寫的各種技巧

3.6 修改原生控件來支持實時預覽

上面也說到了,很多android的原生控件都沒為實時預覽做優化,更不要說第三方的了。在最近的項目中我就遇到了用tablayout做主界面tab欄的需求。但是google設計的tablayout的耦合性太高了,它依賴于一個viewpager,而viewpager又依賴于adapter,adapter又依賴于數據。所以完全沒辦法獨立調試一個tablayout的樣子。因此,我修改了它的代碼,讓其支持了布局的實時預覽。主要就是加入了下面這段代碼:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
private void preview(context context, typedarray a) {
    final string tabstrarr = a.getstring(r.styleable.extablayout_tools_tabstrarray);
    final string[] tabrealstrarr = gettabrealstrarr(tabstrarr);
    viewpager viewpager = new viewpager(context);
    viewpager.setadapter(new pageradapter() {
      @override
      public int getcount() {
        return tabrealstrarr.length;
      }
      @override
      public boolean isviewfromobject(view view, object object) {
        return view == object;
      }
      @override
      public charsequence getpagetitle(int position) {
        return tabrealstrarr[position];
      }
    });
    viewpager.setcurrentitem(0);
    this.setupwithviewpager(viewpager);
  }

你不是要viewpager么,我就給你viewpager。你不是要adapter么,我就給你adapter。你還要數據,好我也給你數據。值得注意的是,如果你這塊代碼是為了實時預覽用,不想對真實的代碼做任何影響,那么請務必用到isineditmode()這個方法,比如上面的代碼是這么調用的:

?
1
2
3
4
// preview
if (isineditmode()) {
  preview(context, a);
}

現在來看看效果吧:

Android UI實時預覽和編寫的各種技巧

這種修改原生控件支持預覽的做法沒什么高深的,大家可以用類似的思路去改造那些難以預覽的控件。

3.7 通過插件來進行動態預覽

我們都知道as的布局預覽只支持靜態預覽,我們不能對預覽界面進行交互,這樣就無法測試滑動效果和點擊效果了。所以我找到了jimu mirror這個插件來支持動態預覽。啟動mirror后,它會在你的手機上安裝一個apk,這個apk展示的就是你當前的布局頁面,mirror會監聽xml文件的改動,如果xml文件發生了變化,那么它就能立刻刷新布局。下面來展示下我是如何在它的支持下預覽viewpager的。

1. 首先在viewpager中加入這段代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
private void preview(context context, attributeset attrs) {
    typedarray a = context.obtainstyledattributes(attrs, r.styleable.exviewpager);
    list<view> viewlist = new arraylist<>();
    int layoutresid;
    if ((layoutresid = a.getresourceid(r.styleable.exviewpager_tools_layout0, 0)) != 0) {
      viewlist.add(inflate(context, layoutresid, null));
    }
    if ((layoutresid = a.getresourceid(r.styleable.exviewpager_tools_layout1, 0)) != 0) {
      viewlist.add(inflate(context, layoutresid, null));
    }
    if ((layoutresid = a.getresourceid(r.styleable.exviewpager_tools_layout2, 0)) != 0) {
      viewlist.add(inflate(context, layoutresid, null));
    }
    if ((layoutresid = a.getresourceid(r.styleable.exviewpager_tools_layout3, 0)) != 0) {
      viewlist.add(inflate(context, layoutresid, null));
    }
    if ((layoutresid = a.getresourceid(r.styleable.exviewpager_tools_layout4, 0)) != 0) {
      viewlist.add(inflate(context, layoutresid, null));
    }
    a.recycle();
    setadapter(new previewpageradapter(viewlist));
  }
  /**
   * @author jack tony
   * 這里傳入一個list數組,從每個list中可以剝離一個view并顯示出來
   * @date :2014-9-24
   */
  public static class previewpageradapter extends pageradapter {
    private list<view> mviewlist;
    public previewpageradapter(list<view> viewlist) {
      mviewlist = viewlist;
    }
    @override
    public int getcount() {
      return mviewlist.size();
    }
    @override
    public boolean isviewfromobject(view arg0, object arg1) {
      return arg0 == arg1;
    }
    @override
    public void destroyitem(viewgroup container, int position, object object) {
      if (mviewlist.get(position) != null) {
        container.removeview(mviewlist.get(position));
      }
    }
    @override
    public object instantiateitem(viewgroup container, int position) {
      container.addview(mviewlist.get(position), 0);
      return mviewlist.get(position);
    }
  }

上面的工作是為xml中設置viewpager中頁面的layout做支持,以達到預覽的作用。

2. 編寫xml布局文件

?
1
2
3
4
5
6
7
8
9
10
11
12
<kale.uidemo.exviewpager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_weight="1.0"
    android:scrollbars="none"
 
    app:scrollable="true"
    app:tools_layout0="@layout/demo_fragment01"
    app:tools_layout1="@layout/demo_fragment02"
    app:tools_layout2="@layout/demo_fragment01"
    />

最后運行插件即可看到效果:

Android UI實時預覽和編寫的各種技巧

四、快速預覽插件

Android UI實時預覽和編寫的各種技巧

上文提到了利用jimu mirror來做ui的實時預覽,更多的預覽技巧可以去他們的網站進行瀏覽。mirror做的是實時替換靜態的xml文件,讓開發者可以在真機中看到ui界面,感興趣的朋友可以去試用體驗版本的mirror。我在體驗后感受到了它的強大和便捷,因為體驗就幾十天,所以我不得不成為了付費用戶。其中最令人喜愛的是,他支持tools標簽的屬性并且支持力度強于as的實時預覽器。

Android UI實時預覽和編寫的各種技巧

與jimu mirror類似的,還有jrebel。這個東西更加強大,它做的不僅僅是讓ui界面實時刷新,它甚至做到了讓你更改java代碼后就能實時替換apk中的類文件,達到應用實時刷新,我認為它是采用了熱替換技術。官網的介紹是:skip build, install and run,因此它可以節約我們很多很多的時間,它的效果也十分不錯。
jrebel和mirror的側重點是不同的,它注重縮短應用整體的調試時間,走的仍舊是真機出結果的路線。而mirror目的是讓開發者能實時預覽ui,走的是ui獨立測試的路線。總體來說這兩款插件都挺不錯的,這簡直是給官方打臉啊。但因為jrebel太貴了,所以我還是推薦大家用mirror。

五、總結

這篇文章確實挺長的,也花了很多功夫。我仍舊覺得官方在設計和優化ide上程序員思維太重,給開發者帶來的便利還是太少。tools標簽一直沒代碼提示、官方的控件的可預覽性不友好等問題也使得開發者很難快速地進行ui調試。在如今android世界mvp、mvvm等模式大行其道的今天,ui獨立測試變得尤為重要,我不希望大家每次調試ui還得安裝運行一遍apk,更加不希望看到as的實時預覽功能變成雞肋。

總之,感謝大家閱讀到最后,如果你有其他的ui調試技巧請指出,如果你覺得本文提出的技巧有用,那么請嘗試。
祝愿大家,雙十一快樂~

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: 亚洲精品短视频 | 欧美白虎逼 | 女明星放荡高h日常生活 | free性泰国女人hd | 腿交hd| 美女逼逼软件 | 99综合视频 | 欧美亚洲第一页 | 日本无卡码一区二区三区 | 国产精品夜夜爽张柏芝 | 亚洲色图色 | 午夜综合网 | 我被男人下药添得好爽 | 爽爽窝窝午夜精品一区二区 | 玩两个少妇女邻居 | 视频一区二区国产无限在线观看 | 五月激激激综合网色播免费 | 毛片在线观看网站 | 新新电影理论中文字幕 | 成人久久18免费网站 | 国产美女操 | 天海翼三级 | 男人天堂日韩 | 久久永久免费视频 | 美女班主任下面好爽好湿好紧 | 国内精品 大秀视频 日韩精品 | 2022色婷婷综合久久久 | 久久棋牌评测 | 91系列在线观看免费 | 日本黄大片影院一区二区 | 把美女屁股眼扒开图片 | 美女脱了内裤打开腿让人羞羞软件 | 亚洲同性男男gay1069 | 久久精品国产在热亚洲 | 国产视频自拍一区 | 日本妇人成熟免费不卡片 | 国产japanese孕妇孕交 | 欧美三级免费观看 | 91韩国女主播 | 久久观看视频 | 午夜性色一区二区三区不卡视频 |