<address id="ttjl9"></address>

      <noframes id="ttjl9"><address id="ttjl9"><nobr id="ttjl9"></nobr></address>
      <form id="ttjl9"></form>
        <em id="ttjl9"><span id="ttjl9"></span></em>
        <address id="ttjl9"></address>

          <noframes id="ttjl9"><form id="ttjl9"></form>

          首頁

          如何設計交互缺省頁?

          資深UI設計者

          缺省頁指頁面的信息內容為空或信息響應異常的狀態;設計缺省狀態的作用不僅是引導用戶在異常邊界狀態的操作提示,同時也是安撫用戶體驗情緒的重要場景;更重要的是為邊界場景營造出良好用戶體驗。通過分析缺省狀態產生的原理,從而更為準確地把握交互缺省頁的設計原則。

          哪些狀態需要缺省頁

          談到缺省頁面可能是設計師最容易忽略輸出的范圍,可能直到對接的開發同學提出來,「這個頁面,如果沒有數據的時候,該怎么顯示啊?」。為了更好地把控設計缺省頁交互狀態,首先要了解缺省頁出現的原理。App 頁面內容(包括圖片、文字、數據字段等等)都是請求服務器數據,順利返回后,正常顯示到客戶端頁面。在了解清楚基礎實現邏輯后,就可以開始梳理、整理缺省狀態的設計思路。

          △ 圖1 缺省狀態的場景梳理圖

          缺省狀態包括:系統層、信息層、空白層。

          系統層:指當用戶請求服務器時,返回提示請求提交失敗,并檢測到失敗原因時呈現的頁面;例如:加載失敗、服務器異常、無網絡等;頁面一般會有重新請求的快捷按鈕。文案上可做失敗原因的細分描述,也可節約成本使用網絡異常的統一文案。

          信息層:請求服務器數據成功,但返回的數據異常的頁面;例如:內容已刪除、內容已下架、內容不存在;文案內容以提示數據類型的缺失為主。顯示形式除了常有的全屏缺省圖,還會出現在數據列表下單一內容缺失的缺省模塊化的情況,例如:單一作品在書架上顯示已下架。

          空白層:請求服務器數據成功,但顯示無數據;內容頁在無數據時需要缺省狀態進行表達;例如:頁面空數據、搜索無結果等??瞻醉撁鎸儆谡>W絡顯示場景,所以一般會在缺省頁附帶有相似屬性模塊的用戶引導,爭取用戶重復消費的目標,滿足用戶的操作的訴求。

          最后根據每個不同的缺省狀態,梳理產品相對應的場景。逐一根據場景特點來設計頁面內容。那缺省頁的設計有哪些表現形式呢?

          缺省頁的表現形式

          沒有用心設計的缺省頁無法給用戶帶來良好用戶體驗,并可能給用戶帶來困擾,如下圖:某小眾直播平臺的拉新邀請頁面,無邀請記錄狀態下沒有任何有效反饋信息,用戶不能明確得知到底是網絡問題還是賬號同步出錯,亦或者是沒有一次邀請。正確的缺省頁設計內容理應明確表達出符合用戶心理預期的視覺場景表達(圖形);和使用易理解和語法恰當地表達當前的異常狀態(標題)甚至于引導用戶解決問題的文案描述。

          △ 圖5 缺省頁的錯誤示范

          1. 視覺圖案+文案

          此類缺省設計形式一般應用于表達系統性無響應或初始空白態的缺省場景。視覺圖案一般使用 app 吉祥物或主色調延展出的 icon 或插畫來表示缺省狀態;文字:通常為「標題」或「標題+描述」結構;標題通常是表達出現缺省的原因;描述文案則說明結束缺省狀態的解決辦法,如「請檢查網絡是否順暢」 等等。

          2. 視覺圖案+文案+引導

          此類缺省設計形式一般運用于需要用戶引導操作來達到業務目標的缺省場景。在視覺圖案+文案的基礎上加入引導模塊,主要作用于避免用戶在數據邊界的狀態下,會因為無法達到操作目的而提高的跳出率。引導模塊的內容包括:相似屬性內容,相似行為目標按鈕或解決缺省狀態操作按鈕,加入引導,用戶進行某項行為或者感知某些信息,對于功能的教學和使用頻率的提升有著重要作用。引導模塊的形式也是日新月異,逐漸變成新用戶業務引導的作用,不僅限于頁面平鋪,也可以做成固定氣泡微動效,例如:抖音的發布缺省頁。

          缺省頁的設計技巧

          缺省頁除了常規的提示型設計方法,還有許多其他的設計技巧,幫助用戶在遇到困難,更好地安撫用戶的情緒。這些設計技巧有些是替代原來的缺省內容,讓用戶有更多的消費空間與深度。有些是拓展缺省狀態的補充內容,讓用戶不容易跳出頁面,增加用戶的消費時長。具體如下:

          1. 使用推薦內容

          缺省狀態中的空白層非常影響邊界情況的用戶體驗,提出一種假設,是否可以刻意推薦相同屬性的內容呢?這樣的界面既不會顯得蒼白無力又可以留住用戶的注意力。相似性的內容也可以解決用戶目標的迫切性。所以說,這種方法非常適合內容型產品中使用。例如:新用戶在打開電商產品的購物車時候,理應是空白無消費行為的操作記錄。那么平臺方通過用戶畫像與熱門排行算法推薦了一個商品流。這樣可以解決用戶無目標性挑選的訴求,增加消費時長。至于產品如果確定用戶畫像的推薦算法,可以通過獲取第三方登錄的個人基本數據之后,才給我推薦了數據庫內相對應標簽的熱門商品,這樣推薦的精準度也會高些。

          2. 使用緩存

          是否使用緩存內容代替缺省狀態?根據產品特性來判斷,工具類、金融類等同類型產品不適合使用緩存;因為用戶交互操作的數據必須保持實時性與真實性。而內容型、電商類等類型產品適合使用緩存來代替缺省狀態;理由:用戶消費內容的轉化路徑是先消費后轉化的行為特點,不存在系統操作門檻,且緩存內容可以代替產品的缺省狀態,安撫用戶操作失敗所帶來跳出率過高的風險。

          3. 情感化表達

          當缺省頁給到用戶時,通常省時省力的做法就是老老實實告訴用戶當前的狀態,最多配上一個具有通識性的灰色 icon。但是,秉持著以用戶體驗為己任的時代,我們其實可以把缺省內容表達得更加生動形象一些。在這里會加入一些情感化的表達,而不是僅僅只是做到準確的目標而已,比如加上活潑的插圖故事,或者把文案寫得更加擬人化、戲劇化一些。這些配圖在讓用戶明白當前的狀態的同時,往往也能引發用戶會心一笑,從而彌補空白頁面帶來的失落感甚至可以帶給用戶一些正面的情感。如下圖:

          4. 提供新任務

          通常缺省頁的引導模塊都著眼于解決當前任務。如果碰到沒有解決方案的情況(例如:404,服務器崩潰等)可以提供給用戶具有情感共情的新任務,讓他們暫時忘記無法達到目標的挫敗感,又有體諒的情懷。幫助建立正向積極的品牌價值觀。例如:訪問騰訊網時訪問失敗的時候,網頁除了顯示 404 狀態之外,還會顯示騰訊「寶貝回家」的公益尋人計劃。將缺省頁與公益內容相結合,不僅改善到用戶缺省狀態。也貫徹騰訊價值觀「用戶為本,科技向善」的輸出。一個好的缺省頁也可以承擔社會責任,讓公益傳播到每個角落。

          △ 圖10 騰訊網404公益任務缺省頁

          結語:作為設計師有時會聽到需求方表述「這種極少出現的情況,我們可以暫且不管它?!沟羌毠澮娬嬲拢袃炐愕捏w驗設計都必須照顧到方方面面的缺省情況。讓每個用戶的流量價值發揮到最大,產生相互信任的良好的品牌關系。這樣的平臺生態是良性的,這樣的產品會更有流量轉化的商業化價值。

          文章來源:優設    作者:騰訊動漫TCD

          uni-app uni.request接口封裝

          seo達人

          uni-app uni.request接口封裝

          今天在做uni-app項目時,發現在uni-app中 調取后臺接口需要大量的重復編輯,就在想能不能封裝一個如同Vue項目中的this.$axios.get(url,data).then();格式,這樣就減少了很多代碼重復??!

          封裝為如同this.$axios.get(url,data).then();格式

          第一步、

          我們先在index首頁中的組件部分,創建一個js文件;





          第二步、

          我們在uni-app的入口文件中引入request.js文件;

          在入口文件中掛載到uni-app實例上;





          第三步、

          開始接口封裝:

          (以下為js文件代碼)



          //先把接口暴露出去

          export default{

          //我們先定一個uni-app方法 以便于以下操作使用uni-app調取接口時便利

          request(options){

          ///我們使用Promise方法來實現調用接口時后面多個.then()的方法

          //只有Promise能實現如同$axios后面連續多個.then()的方法

          return new Promise((reslove,reject)=>{

          uni.request({

          ...options,

          success:res=>{

          //判斷我們在使用封裝的自定義時第三個參數是否為native

          //當native為true時 我們返回原數據

          if(options.native){

          reslove(res)

          }

          //當native為false時 我們直接返回data中的數據

          if(res.statusCode === 200){

          reslove(res.data)

          }else{

          //加入接口參數錯誤或接口地址錯誤時 我們返回原錯誤提示

          reject(res)

          }

          }

          })

          })

          },

          //在方法中 第二個參數和第三個參數用ES6新語法來添加默認值

          //接口調取get方法

          get(url,data={},options={}){

          //我們把傳過來的參數賦給options,這樣我們在使用uni-app

          //this.request()方法時 傳遞一個參數就可以

          options.url = url;

          options.data = data;

          options.method = 'get';

          //調用上面自己定義的this.request()方法傳遞參數

          return this.request(options)

          },

          //接口調取post方法

          post(url,data={},options={}){

          options.url = url;

          options.data = data;

          options.method = 'post';

          return this.request(options)

          }

          }



          這樣我們就已經封裝完成啦,接下來就是 在頁面內使用!

          第四步、

          我們可以在頁面中來調取已經封裝好的自定義事件啦



          例一:

          個人建議使用ES6新語法 箭頭函數 不然使用this還要重新在外面聲明定義,太麻煩了,使用箭頭函數就會方便很多



          // 已封裝好的接口方法

          //本案例調取接口時 沒有參數上傳 直接調用的

          //這樣使用方法時只傳遞了一個參數,也就是接口地址

          //第二個參數沒有寫,默認為空;假如有參數的話 可以直接填寫

          //后面的參數都為接口內已經定義好的默認值:{}空對象

          //里面的res為接口返回數據中的data里面的內容

          this.$H.get('/api/getIndexCarousel.jsp').then(res=>{

          //res打印出來是接口返回數據中data里面的數據

          console.log(res)

          //賦給數據區的變量,方便本頁面使用

          this.swiperData = res

          });



          例二、



          // 已封裝好的接口方法

          //本案例使用時 傳遞了三個參數

          //第一個為:接口地址

          //第二個為:調取接口傳遞的參數,方法使用時不用傳參,寫空對象就好

          //第三個為:自定義事件中 native 的屬性 若為true 則返回原數據

          //若想返回原數據,必須要填寫第二個參數,若沒有參數,也要寫空對象

          //因為方法調用時 是按照傳參順序調用的,若不寫 參數傳遞就會出錯

          this.$H.get('/api/getIndexCarousel.jsp',{},{

          native:true

          }).then(res=>{

          //res打印出來的數據是接口返回來的原數據

          console.log(res)

          //賦給數據區的變量,方便本頁面使用

          this.swiperData = res

          });




          每天學習一個Android中的常用框架——1.Litepal

          seo達人

          文章目錄

          1.簡介

          2.特性

          3.演示

          3.1 集成

          3.2 配置

          3.3 創建數據庫

          3.4 升級數據庫

          3.5 插入數據

          3.6 查詢數據

          3.7 更新數據

          3.8 刪除數據

          4.版本異同

          5.源碼地址

          1.簡介

          Litepal——作為帶我入行的第一本教學書籍《Android第一行代碼》的作者郭霖老師所寫出來的持久化框架,幾乎算是我接觸Android世界之后第一個遇到的框架,故將該框架列為一系列學習框架博客的首位。

          根據Litepal的GitHub主頁:Litepal,可以看到該框架的一些簡介:



          LitePal is an open source Android library that allows developers to use SQLite database extremely easy. You can finish most of the database operations without writing even a SQL statement, including create or upgrade tables, crud operations, aggregate functions, etc. The setup of LitePal is quite simple as well, you can integrate it into your project in less than 5 minutes.



          事實上,正如這段簡介所說,集成Litepal相當簡單,不需要超過五分鐘時間。使用Litepal,也適合對sql語言還不熟悉的開發者快速上手。



          2.特性

          讓我們繼續瀏覽Litepal的GitHub主頁,可以發掘Litepal的一些特性:



          Using object-relational mapping (ORM) pattern.

          Almost zero-configuration(only one configuration file with few properties).

          Maintains all tables automatically(e.g. create, alter or drop tables).

          Multi databases supported.

          Encapsulated APIs for avoiding writing SQL statements.

          Awesome fluent query API.

          Alternative choice to use SQL still, but easier and better APIs than the originals.

          More for you to explore.

          用大白話來描述的話,可以列舉如下:



          Litepal使用了ORM(對象關系映射)模型

          Litepal幾乎是無配置的,僅需極少的配置文件

          Litepal幾乎包括所有的CRUD操作,也支持多張表格的操作

          Litepal可以僅調用api進行CRUD操作而避免編寫sql語句

          總之,看到Litepal具有這么多良好的特性,讀者是否心動了呢。理論的話不多說,我們現在就開始正式地使用Litepal進行數據庫的相關操作

          PS:如果有曾經學習過Java的ORM框架——Mybatis的讀者,應該不會對Litepal的使用太陌生,因為它們都使用了xml文件進行相應的配置



          3.演示

          3.1 集成

          現在Android框架的集成相比于IDE還為ADT的時代,要方便了許多。原因是現在的主流IDE是Android Studio,而AS默認使用了Gradle進行版本的配置管理,這讓集成框架變得簡單了許多。

          在build.gradle下,添加以下語句,然后重新sync,即可將Litepal集成到你的項目中:



          implementation 'org.litepal.android:java:3.0.0'

          1

          當然,目前Android的主流開發語言,除了Java之外,還有Kotlin,Litepal同樣具有Kotlin版本的(這里的演示僅針對Java,Kotlin版本的異曲同工)依賴:



          implementation 'org.litepal.android:kotlin:3.0.0'

          1

          可以根據個人需求進行配置。



          3.2 配置

          集成了Litepal之后,要想正式使用它還需要進行一些配置



          在assets目錄下新建litepal.xml,作為Litepal的全局配置文件,相應的條目信息已作出注釋,代碼如下:

          <?xml version="1.0" encoding="utf-8"?>

          <litepal>

              <!--  數據庫名  -->

              <dbname value="androidframelearn"/>



              <!--  數據庫版本號  -->

              <version value="1"/>



              <!--  指定映射模型  -->

              <list>

                 

              </list>



              <!--  指定文件的存儲方式  -->

              <!--  <storage value="external" />-->

          </litepal>



          在你的應用下配置Litepal,有兩種方式可以實現:

          修改清單文件,將你的應用名修改為:android:name="org.litepal.LitePalApplication"

          新建一個自己寫的MyOwnApplication類,然后將清單文件中的應用名定位到該類,即:android:name="com.example.MyOwnApplication",然后再編寫MyOwnApplication類,代碼如下:

          public class MyOwnApplication extends Application {



          @Override

          public void onCreate() {

              super.onCreate();

              LitePal.initialize(this);

          }

          ...

          }



          兩種方式亦可,Litepal的作者建議若使用第二種方式,需要盡快地調用LitePal.initialize(this);所以將其放在onCreate()方法是最好的。



          3.3 創建數據庫

          剛才在介紹的時候已經說過,Litepal采取的是對象關系映射(ORM)的模式,那么什么是對象關系映射呢?簡單點說,我們使用的編程語言是面向對象語言,而使用的數據庫則是關系型數據庫,那么將面向對象的語言和面向關系的數據庫之間建立一種映射關系,這就是對象關系映射了。

          不過你可千萬不要小看對象關系映射模式,它賦予了我們一個強大的功能,就是可以用面向對象的思維來操作數據庫,而不用再和SQL語句打交道了,不信的話我們現在就來體驗一下。像往常使用SQLiteOpenHelper類,為了創建一張Book表需要先分析表中應該包含哪些列,然后再編寫出一條建表語句,最后在自定義的SQLiteOpenHelper中去執行這條建表語句。但是使用LitePal,你就可以用面向對象的思維來實現同樣的功能了,定義一個Book類,代碼如下所示:



          package com.androidframelearn.dao_litapal;



          import org.litepal.crud.LitePalSupport;



          public class Book extends LitePalSupport {

              private int id;

              private String author;

              private double price;

              private int pages;

              private String name;

              public int getId(){

                  return id;

              }

              public void setId(int id){

                  this.id = id;

              }



              public String getAuthor(){

                  return author;

              }

              public void setauthor(String author){

                  this.author = author;

              }



              public double getPrice(){

                  return price;

              }

              public void setPrice(double price){

                  this.price = price;

              }



              public int getPages(){

                  return pages;

              }

              public void setPages(int pages){

                  this.pages = pages;

              }



              public String getName(){

                  return name;

              }

              public void setName(String name){

                  this.name = name;

              }

          }



          這里使用標簽來聲明我們要配置的映射模型類,注意一定要使用完整的類名。不管有多少模型類需要映射,都使用同樣的方式配置在標簽下即可。

          沒錯,這樣就已經把所有工作都完成了,現在只要進行任意一次數據庫的操作,BookStore.db數據庫應該就會自動創建出來。為了更好地演示代碼,我們將布局文件所需要的功能一次性編寫好,activity_main.xml代碼如下:



          <?xml version="1.0" encoding="utf-8"?>

          <LinearLayout 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:context=".MainActivity"

              android:orientation="vertical">



              <Button

                  android:id="@+id/btn_db_create"

                  android:layout_width="match_parent"

                  android:layout_height="wrap_content"

                  android:text="創建數據庫"/>



              <Button

                  android:id="@+id/btn_db_query"

                  android:layout_width="match_parent"

                  android:layout_height="wrap_content"

                  android:text="查詢數據"/>



              <Button

                  android:id="@+id/btn_db_insert"

                  android:layout_width="match_parent"

                  android:layout_height="wrap_content"

                  android:text="插入數據"/>



              <Button

                  android:id="@+id/btn_db_update"

                  android:layout_width="match_parent"

                  android:layout_height="wrap_content"

                  android:text="更新數據"/>



              <Button

                  android:id="@+id/btn_db_delete"

                  android:layout_width="match_parent"

                  android:layout_height="wrap_content"

                  android:text="刪除數據"/>



          </LinearLayout>





          接下來,修改MainActivity,除了給按鈕注冊點擊事件,還需要編寫不同的方法代表不同的邏輯,其中,創建數據庫的方法代碼如下:



          private void createDBbyLitePal() {

                  btn_db_create.setOnClickListener(new View.OnClickListener() {

                      @Override

                      public void onClick(View v) {

                          Log.i(TAG,"創建數據庫成功");

                          LitePal.getDatabase();

                      }

                  });

              }



          僅僅通過點擊按鈕,調用LitePal.getDatabase();這句api,就可以創建出數據庫,讓我們實際進入項目中嘗試一下吧!點擊該按鈕,然后查看控制臺,如圖所示:



          出現該句日記,說明數據庫創建成功,接下來我們看看這個數據庫是否按照我們所設置好的格式創建出來了,進入data/data/你的項目包名/databases,即可查看到該數據庫已經放置到該目錄下,如圖所示:





          3.4 升級數據庫

          事實上,若想對現有數據庫進行升級,也是可以實現的。以前我們使用SQLiteOpenHelper來升級數據庫的方式,雖說功能是實現了,但你有沒有發現一個問題,,就是升級數據庫的時候我們需要先把之前的表drop掉,然后再重新創建才行。這其實是一個非常嚴重的問題,因為這樣會造成數據丟失,每當升級一次數據庫,之前表中的數據就全沒了。

          而使用Litepal,就可以很好地避免這個問題。假設我們現在有一張新的表Category要加進去,同樣編寫它的實體類,代碼如下:



          package com.androidframelearn.dao_litapal;



          public class Category {

              private int id;

              private String categoryName;

              private int categoryCode;

              public int getId(){

                  return id;

              }

              public void setId(int id){

                  this.id = id;

              }



              public String getCategoryName(){

                  return categoryName;

              }

              public void setCategoryName(String categoryName){

                  this.categoryName = categoryName;

              }



              public int getCategoryCode(){

                  return categoryCode;

              }

              public void setCategoryCode(int categoryCode){

                  this.categoryCode = categoryCode;

              }

          }



          改完了所有我們想改的東西,只需要記得在litepal.xml將版本號加1就行了。當然由于這里還添加了一個新的模型類,因此也需要將它添加到映射模型列表中。修改litepal.xml中的代碼,如下所示:



          <?xml version="1.0" encoding="utf-8"?>

          <litepal>

              <!--  數據庫名  -->

              <dbname value="androidframelearn"/>



              <!--  數據庫版本號  -->

              <version value="2"/>



              <!--  指定映射模型  -->

              <list>

                  <mapping class="com.androidframelearn.dao_litapal.Book"/>

                  <mapping class="com.androidframelearn.dao_litapal.Category"/>

              </list>



              <!--  指定文件的存儲方式  -->

              <!--  <storage value="external" />-->

          </litepal>



          重新運行一下程序,再次創建數據庫,就可以完美地完成數據庫的升級了。這里的調試可以使用sqlite工具,這里不再贅述。



          3.5 插入數據

          在講述本節時,首先回顧一下之前添加數據的方法,我們需要創建出一個Contentvalues對象,然后將所有要添加的數據put到這個Contentvalues對象當中,最后再調用SQLiteDatabase的insert() 方法將數據添加到數據庫表當中,步驟相當繁瑣。

          而使用LitePal來添加數據,這些操作可以簡單到讓你驚嘆!我們只需要創建出模型類的實例,再將所有要存儲的數據設置好,最后調用一下save()方法就可以了。

          同樣地,修改MainActivity,增加插入數據的事件方法,代碼如下:



          private void insertDatabyLitePal() {

                  btn_db_insert.setOnClickListener(new View.OnClickListener() {

                      @Override

                      public void onClick(View v) {

                          Book book = new Book();

                          book.setName("The Da Vinci Code");

                          book.setauthor("Dan Brown");

                          book.setPages(454);

                          book.setPrice(16.96);

                          book.save();

                          Log.i(TAG,"插入數據成功");

                      }

                  });

              }



          同樣運行程序,查看控制臺,如圖所示:



          當點擊查詢數據(下一節將介紹該邏輯)時,控制臺打印剛剛插入的數據,如圖所示:





          3.6 查詢數據

          使用Litepal同樣可以很輕易地查詢數據,當然了,由于篇幅限制,這里僅僅貼出最簡單的查詢方式,至于關聯查詢等稍復雜的查詢方式,可以去GItHub上參考Litepal的官方文檔進行相關調用即可。

          同樣地,修改MainActivity,增加查看數據的事件方法,代碼如下:



          private void queryDatabyLitePal() {

                  btn_db_query.setOnClickListener(new View.OnClickListener() {

                      @Override

                      public void onClick(View v) {

                          List<Book> books = LitePal.findAll(Book.class);

                          for (Book book : books){

                              Log.i(TAG,"查詢數據成功");

                              Log.d("MainActivity","書名是"+book.getName());

                              Log.d("MainActivity","書的作者是"+book.getAuthor());

                              Log.d("MainActivity","書的頁數是"+book.getPages());

                              Log.d("MainActivity","書的價格是"+book.getPrice());

                          }

                      }

                  });

              }



          相關的運行結果上一小節以貼出,這里不再重復。



          3.7 更新數據

          更新數據要比添加數據稍微復雜一點,因為它的API接口比較多,這里我們只介紹最常用的幾種更新方式。

          首先,最簡單的一種更新方式就是對已存儲的對象重新設值,然后重新調用save()方法即可。那么這里我們就要了解一個概念,什么是已存儲的對象?

          對于LitePal來說,對象是否已存儲就是根據調用model.isSaved()方法的結果來判斷的, 返回true就表示已存儲,返回false就表示未存儲。那么接下來的問題就是,什么情況下會返回true,什么情況下會返回false呢?

          實際上只有在兩種情況下model.isSave()方法才會返回true, 一種情況是已經調用過model. save()方法去添加數據了,此時model會被認為是已存儲的對象。另一種情況是model對象是通過LitePal提供的查詢API查岀來的,由于是從數據庫中查到的對象,因此也會被認為是已存儲的對象。

          由于查詢API相對復雜,因此只能先通過第一種情況來進行驗證。修改MainActivity中的代碼,如下所示:



          private void updateDatabyLitePal() {

                  btn_db_update.setOnClickListener(new View.OnClickListener() {

                      @Override

                      public void onClick(View v) {

                          Book book = new Book();

                          book.setName("The Lost Symbol");

                          book.setauthor("Dan Brown");

                          book.setPages(510);

                          book.setPrice(19.95); // 第一次設置商品價格

                          book.save();

                          book.setPrice(10.99); // 第二次設置商品價格

                          book.save();

                          Log.i(TAG,"更新數據成功");

                      }

                  });

              }



          可以看到,我們做了跟插入數據類似的事情,但是我們對數據的價格進行了設置,運行程序,如圖所示:



          可以看到,除了剛剛插入的數據,還有第二條剛剛更新過后的數據。然而這種更新方式只能對已存儲的對象進行操作,限制性比較大,接下來我們學習另外一種更加靈巧的更新方式,可以調用以下api:



          book.updateAll("name = ? and author = ?","The Lost Symbol","Dan Brown");

          1

          這里僅貼出其中一條api,其他的可以參考官方文檔,這里不再贅述。



          3.8 刪除數據

          使用Litepal刪除數據的方式主要有兩種,第一種比較簡單,就是直接調用已存儲對象的delete()方法就可以了,對于已存儲對象的概念,我們在之前已經學習過了。也就是說,調用過save()方法的對象,或者是通過LitePal提供的查詢API查出來的對象,都是可以直接使用delete()方法來刪除數據的。這種方式比較簡單,我們就不進行代碼演示了,下面直接來看另外一種刪除數據的方式。

          代碼如下:



          private void deleteDatabyLitePal() {

                  btn_db_delete.setOnClickListener(new View.OnClickListener() {

                      @Override

                      public void onClick(View v) {

                          LitePal.deleteAll(Book.class,"price < ?","15");

                          Log.i(TAG,"刪除成功");

                      }

                  });

              }



          運行程序,刪除過后,按照代碼邏輯,已經刪除掉了所有price小于15的條目,如圖所示:





          4.版本異同

          之前閱讀了郭霖老師所著《Android第一行代碼 第二版》時,所記載的Litepal版本為:



          compile 'org.litepal.android:core:1.4.1'

          1

          而的Litepal版本(Java版本,另有Kotlin版本,導入的依賴稍有不同)為:



          implementation 'org.litepal.android:java:3.0.0'

          1

          新舊版本的主要區別是一些類名的劃分,例如老板本的DataSupport變成了LitePalSupport,除此之外,一些api的名稱也稍有變動,讀者在使用時最好可以參考GitHub上的官方文檔,及時更新代碼,做到與時俱進。



          5.源碼地址

          AFL——Android框架學習


          TinyUI-TUIListView最簡單的使用

          seo達人

                在TinyUI簡介的博客中提到其特點中包含一條,即多數大控件的使用方法和android一直,除了語言差異之外,本篇我們就介紹列表控件TUIListView最簡單的使用方法。



                  列表組件/控件作為目前Android/iOS的APP中最常用的控件,該控件的設計同時參考Android、windows、Qt等使用的經驗進行篩選,最終選擇了Android的ListView設計,其他平臺的列表中使用難以程度或設計上略遜于Android,因為Android給與了開發者最大的發揮控件,你可以在列表中可以顯示任何控件。



                  TUIListView中的每一行每一列你可以放置任何UI組件,使用TUIListView需要配合TUIAdapter進行使用,而TinyUI和Android一樣提供了內置的簡單使用的TUISimpleAdapter。TUISimpleAdapter主要用于顯示文本(即每一行都是只能顯示文字),如果需要在列表中顯示其他UI組件,則需要自定義一個Adapter,關于自定義Adapter將在后續進行詳細講解。



                  本篇既然是TUIListView最簡單的使用,我們就使用TUISimpleAdapter來顯示文本你列表,TUISimpleAdapter最好只用于數據步發生變化的情況,因為其存放的數據使用了C++標準庫的vector容器,而非使用list容器,vector容器的特點是訪問速度快,但其缺點是vector的內存是連續的,因此內容發生變化可能會造成內存申請和拷貝的動作;而list容器使用的雙向鏈表,其特點是插入數據快,但訪問速度慢。



                  本篇我們仍然使用上一篇中自定義的MyWindow來顯示TUIListView。



          使用方法/步驟

          1. 定義listView和andapter



                    MyWindow中包含TUISimpleAdapter.h的頭文件,并定義listView和adapter



            MyWindow.h


            ifndef MY_WINDOW_H

            define MY_WINDOW_H

            include <TUIWindow.h>

            include <TUISimpleAdapter.h>

             

             

             

            class MyWindow : public TUIWindow

            {

            public:

                MyWindow(TUIWindow* parent = nullptr);

                virtual ~MyWindow();

             

                void onShow();

                void onClose();

             

            private:

                TUIListView listView;

                TUISimpleAdapter adapter;

            };

             

            endif // !MY_WINDOW_H

             


          2. 填充數據,并把adapter設置到listView中



            MyWindow.cpp


            include "MyWindow.h"

             

             

             

            MyWindow::MyWindow(TUIWindow* parent)

                : TUIWindow(parent)

            {

                setContentView(&this->listView); // 把listView作為當前窗口的內容視圖

             

             

                vector<string> data; // 使用vector<string>類型的data存放數據

             

                for (int32_t i = 0; i < 20; i++)

                {

                    data.push_back(to_string(i)); // 生成0~20的數值-轉換成字符串,放到data中

                }

             

                this->adapter.setData(data); // 把data設置到adapter中

             

                this->listView.setAdapter(&this->adapter); // 把adapter設置到listView,作為listView數據來源和操作對象

            }

             

            MyWindow::~MyWindow()

            {

            }

             

            void MyWindow::onShow()

            {

            }

             

            void MyWindow::onClose()

            {

            }

            到目前為止窗口顯示列表控件已全部完成,接下來和上一篇一樣調用MyWindow的show()方法即可顯示,最終結果如下圖所示:


          call、apply、bind 原理實現

          seo達人

          目錄

          1. call 的模擬實現
          2. apply 的模擬實現
          3. bind 的模擬實現
          4. 三者異同



            學習并參考于:



            JavaScript深入之call和apply的模擬實現



            JS的call,apply與bind詳解,及其模擬實現





            (一)call的模擬實現

            call 用法 : MDN Function.prototype.call()



            call() 方法使用一個指定的 this 值和可選的參數列表來調用一個函數。



            call() 提供新的 this 值給當前調用的函數/方法。


          5. call 實現主要思路:

            將函數設為對象的屬性



            執行該函數



            刪除該函數



            另外還有考慮:



            call 函數還能給定參數執行函數

            this 參數不傳,或者傳null,undefined, this指向window對象

            函數是可以有返回值的
          6. 實現:

            Function.prototype.myCall = function () {

              if (typeof this !== 'function') {

                throw new TypeError('error!')

              }

              let context = arguments[0] || window   //this 參數可以傳 null,當為 null 的時候,視為指向 window

              context.fn = this  // 首先要獲取調用call的函數,用this可以獲取

              let args = [...arguments].slice(1) //從 Arguments 對象中取值,取出第二個到最后一個參數   

              let result = context.fn(...args)  //函數是可以有返回值的

              delete context.fn

              return result

            }


          7. 測試:

            // 測試一下上面實現的myCall

            var value = 2;



            var obj = {

                value: 1

            }



            function bar(name, age) {

                console.log(this.value);

                return {

                    value: this.value,

                    name: name,

                    age: age

                }

            }



            bar.call(null); // 2



            console.log(bar.myCall(obj, 'kevin', 18));

            // 1

            // Object {

            //    value: 1,

            //    name: 'kevin',

            //    age: 18

            // }



            (二)apply 的模擬實現

            apply 用法:MDN Function.prototype.apply()



            apply() 方法使用一個指定的 this 值和可選的參數數組 來調用一個函數。



            apply 的實現跟 call 類似。


          8. 實現:

            Function.prototype.myApply = function () {

              if (typeof this !== 'function') {

                throw new TypeError('error!')

              }

              let context = arguments[0] || window

              context.fn = this

              let result = arguments[1] ? context.fn(...arguments[1]) : context.fn()

              delete context.fn

              return result

            }


          9. 測試:

            var foo = {

                value: 1

            }

            function bar(name, age) {

                console.log(name)

                console.log(age)

                console.log(this.value);

            }

            bar.myApply(foo, ['black', '18']) // black 18 1



            (三)bind 的模擬實現

            bind 用法:MDN Function.prototype.bind()



            bind()方法會創建一個新函數,稱為綁定函數。當這個新函數被調用時,bind() 的第一個參數將作為它運行時的 this,之后的一序列參數將會在傳遞的實參前傳入作為它的參數。



            bind是ES5新增的一個方法,不會執行對應的函數,而是返回對綁定函數的引用。


          10. 實現:

            Function.prototype.customBind = function () {

              if (typeof this !== 'function') {

                throw new TypeError('error!')

              }

              const that = this   // 首先要獲取調用bind的函數,用this獲取并存放在that中

              let context = arguments[0] || window

              const args = [...arguments].slice(1)

              return function() {

                return that.apply(context, args.concat([...arguments]))

              }

            }



            (四)三者異同
          11. 相同:

            改變函數體內 this 的指向
          12. 不同:

            call、apply的區別:call方法接受的是參數列表,而apply方法接受的是一個參數數組。

            bind不立即執行。而call或apply會自動執行對應的函數。


          JavaScript 的簡述與基礎語法

          前端達人

          目錄

          JavaScript

          1. JS 發展歷史
          2. JS 的特點
          3. JS 的組成
          4. JS 的基礎語法

            a. 兩種引入方式 type src

            b. 三種輸出方式 console.log document.write alert

            c. 變量聲明 var let const typeof undefined

            d. 數據類型簡介 string number boolean object undefined

            e. 運算符 + - * / % = < > && || !

            i. 全等符與不全等符 === !==

            f. 流程控制語句

            i. 條件語句 if else switch case default break

            ii. 循環語句 while dowhile fori forin forof



            JavaScript

            • JS 用于完成頁面與用戶的交互功能;

            1. JS 發展歷史
            JavaScript 在 1995 年由 Netscape 公司的 Brendan Eich,在網景導航者瀏覽器上首次設計實現而成。因為 Netscape 與 Sun 合作,Netscape 管理層希望它外觀看起來像 Java,因此取名為 JavaScript。但實際上它的語法風格與 Self 及 Scheme 較為接近;
            歐洲計算機制造聯盟(ECMA)在 1997 制定腳本語言規范 ECMA Script1 (ES1),2009 年發布了 ECMA Script5(ES5),在 2015 年發布了 ECMA Script 2015(ES6),所有的瀏覽器的都支持 ES6;

          5. JS 的特點

            JS 設計模仿了 Java 語言,不同如下:

            JS 不需要編譯,由瀏覽器直接解釋執行;

            JS 是弱類型語言,JS 變量聲明不需要指明類型,不同類型的數據可以賦值給同一變量;
          6. JS 的組成

            ECMA Script 是 JS 的基礎語法;

            BOM(Brower Object Model)是瀏覽器對象模型;

            DOM(Document Object Model)是文檔對象模型;
          7. JS 的基礎語法

            a. 兩種引入方式 type src




            <!DOCTYPE html>
            <html lang="zh">
            <head>
            <meta charset="UTF-8">
            <title>JS兩種引入方式</title>
            </head>
            <body>
            <!-- JS兩種引入方式:JS和CSS一樣都需要引入到html頁面中,瀏覽器才會解釋執行,JS有兩種引入方式:
                1.內嵌(內部)腳本:在script標簽中寫js代碼,script標簽推薦放置在body標簽的底部,理論上和style標簽一樣可以放置位置任意;
                2.外部腳步:使用script標簽的src屬性引入外部js文件,使用注意事項: script標簽使用了src屬性以后內部的代碼不再被瀏覽器解釋執行,script引入外部腳本時不能使用自閉合格式 -->
            <!--告訴瀏覽器把解析器切換為js解析器 type="text/javascript"可以省略-->
            <script type="text/javascript"> document.write("<h1>內部腳本</h1>");//向body中追加html內容 </script>
            <script src="../js/外部腳本.js"></script>
            </body>
            </html>
            






            b. 三種輸出方式 console.log document.write alert

            <!DOCTYPE html>
            <html lang="zh">
            <head>
            <meta charset="UTF-8">
            <title>JS三種輸出方式</title>
            </head>
            <body>
            <!-- JS三種輸出方式:
                1.輸出到瀏覽器控制臺;
                2.輸出html內容到頁面;
                3.瀏覽器彈框輸出字符 -->
            <script>
            //1.輸出到瀏覽器控制臺
            console.log("1. 輸出到瀏覽器控制臺");//開發者專用
            //2.輸出html內容到頁面
            document.write("2. 輸出html內容到頁面");//向body中追加html內容
            //3.瀏覽器彈框輸出字符
            alert("3. 瀏覽器彈框輸出字符");//阻塞執行
            </script>
            </body>
            </html>
            


            c. 變量聲明 var let const typeof undefined


            <!DOCTYPE html>
            <html lang="zh">
            <head>
            <meta charset="UTF-8">
            <title>JS變量</title>
            </head>
            <body>
            <!-- JS變量用來存放數據;
                es5以前變量聲明使用var;
                es6之后變量聲明使用let,常量聲明使用const。他們用于替代es6的var聲明方式;
             JS是弱類型語言: 
                聲明變量時不知道變量的類型(undefined),只有在賦值之后js變量才確定類型;
                typeof(a) 或 typeof a 輸出變量的類型;
                undefined表示變量未賦值,未知類型 -->
            <script>
            //字符串 Java聲明 String str ="張三";
            let str ="張三";
            console.log(str);
            //整數 Java聲明 int k = 5;
            let k = 5;
            console.log(k);
            //小數 Java聲明 float f = 7.5;
            let f = 7.5;
            console.log(f);
            //常量 Java聲明 final Integer PI = 3.14;
            const PI = 3.14;
            console.log(PI);
            //演示弱類型語言
            let a;//聲明變量不需要指明類型
            console.log(typeof a);//undefined 未賦值類型,未知類型
            a = "你好";
            console.log(typeof a);//string
            a = 123;
            console.log(typeof a);//number
            a = true;
            console.log(typeof a);//boolean
            a = new Object();
            console.log(typeof a);//object
            </script>
            </body>
            </html>
            


            d. 數據類型簡介 string number boolean object undefined


            <!DOCTYPE html>
            <html lang="zh">
            <head>
            <meta charset="UTF-8">
            <title>JS數據類型</title>
            </head>
            <body>
            <!-- JS數據類型,常用數據類型:
                1.string 字符串類型;
                2.number 數字.包括整數和小數類型;
                3.boolean 布爾類型.值只有true和false兩個;
                4 object 對象類型,空對象使用null表示,有兩種格式:
                    new Object(); 
                    JSON格式.例如:{name:"張三",age:18};
                5.undefined 變量未賦值 -->
            <script>
            //1. string 字符串
            let str = "你好";
            console.log(str);
            console.log(typeof str);//string
            // 2. number 數字
            let n = 123.456;
            console.log(n);
            console.log(typeof n);//number
            // 3. boolean 布爾類型
            let boo = false;
            console.log(boo);
            console.log(typeof boo);//boolean
            // 4. object 對象類型,空對象使用 null表示
            let obj = null;//或 new Object();
            console.log(obj);
            console.log(typeof obj);//object
            // 5. undefined 變量未賦值
            let u = undefined;
            console.log(u);//值是undefined
            console.log(typeof u);//類型是undefined
            // Object類型
            let stu = new Object();//創建一個js對象,js對象的屬性想要直接加上
            stu.id = 1;
            stu.name = "劉一";
            stu.age = 18;
            console.log(stu);//{id: 1, name: "劉一", age: 18}
            console.log(typeof stu);//object
            // JS對象取屬性值有兩種方式:
            // 1. obj.key
            console.log(stu.name);//劉一
            // 2. obj["key"]
            console.log(stu["name"]); //劉一 == stu.name
            let b = "age";
            console.log(stu[b]);//可以取不定屬性的值
            </script>
            </body>
            </html>
            


            e. 運算符 + - * / % = < > && || !


            i. 全等符與不全等符 === !==


            <!DOCTYPE html>
            <html lang="zh">
            <head>
            <meta charset="UTF-8">
               <title>JS運算符</title>
            </head>
            <body>
            <!--
            JS運算符
            js運算符和Java運算符基本相同
            只有一個特殊的比較運算符
            === 判斷js變量的值和類型都相等才為true
            !== 不全等,判斷js變量的值和類型有一個不等就為true
            -->
            <script> let a = 3;
            let b = "3";
            console.log(a == b);//true
            // 全等 運算符 ===
            console.log(a === b);//false
            // 不全等 運算符 !==
            console.log(a !== b);//true
            // 三元(三目)運算符 布爾表達式?真:假
            let str = a===b?"全等":"不全等";
            console.log(str);//不全等
            </script>
            </body>
            </html>
            


            f. 流程控制語句

            i. 條件語句 if else switch case default break


            <!DOCTYPE html>
            <html lang="zh">
            <head>
            <meta charset="UTF-8">
            <title>條件語句</title>
            </head>
            <body>
            <!-- 條件語句JS的條件語句和Java語法基本一樣,但是對數據類型的真假判斷有些區別 JS中對各種數據類型作為布爾值的特點:(重點掌握) 1. string 空字符串""為false,其余都為true 2. number 數字 只有0為false,其余數字都為true 3. boolean 布爾類型 值只有 true和false 兩個
            循環語句
          8. object 對象類型 空對象null表示false,其它對象都是true 5. undefined 變量未賦值 為false 常用語法格式 if ... else if ... else switch case break default -->
            <script>
            //if ... else
            //if(true){
            //if(""){// string 只有空字符為假
            //if(0){number 只有0為假
            //if(false){//boolean false為假 true為真
            //if(null){//object null為假
            //if(undefined){//undefined永為假
            if("undefined"){//undefined永為假
            console.log("滿足條件");
            }else{
            console.log("不滿足條件");
            }

            //switch case break default
            let k =1;
            switch (k) {
            case 1:
            console.log("111");break;
            case 2:
            console.log("222");break;
            default: console.log("其它情況"); }
            </script>
            </body>
            </html>


            ii. 循環語句 while dowhile fori forin forof


            <!DOCTYPE html>
            <html lang="zh">
            <head>
            <meta charset="UTF-8">
            <title>循環語句</title>
            </head>
            <body>
            <!-- 循環語句
                while,do while,fori 和Java一樣;
                forin
                    1.遍歷出數組中的索引和元素
                    2.遍歷出對象中的屬性和元素
                forof 
                    1.遍歷出數組中的元素
                forin 與 forof 區別:
                    1.forin可以遍歷對象,forof不能遍歷對象
                    2.forin可以遍歷出數組中的索引,forof只能遍歷出數組中的元素 -->
            <script>
            //while 和Java一樣
            let k=1;
            while (k<3){
                console.log(k++);
            }
            
            //do while 和Java一樣
            k =1;
            do{
                console.log(k++);
            }while (k<3)
            
            //fori 和Java一樣
            for(let i=0;i<3;i++){
                console.log(i);
            }
            
            //forin 可以遍歷數組和對象
            let arr = ["劉一","陳二","張三"];//JS數組使用中括號[]定義
            let stu = {id:5,name:"李四",age:18};//JS對象使用大括號定義
                //1.forin 遍歷出數組中的索引
            for(let index in arr){
                console.log(index);//數組的索引 0,1,2
                console.log(arr[index]);//數組中的元素
            }
                //2.forin 遍歷出對象中的屬性名key
            for(let k in stu){
                console.log(k);//字符串屬性 id,name,age
                console.log(stu[k]);//對象中的屬性值
            }
            
            //forof 可以遍歷數組
            for(let e of arr){
                console.log(e);//數組中的元素
            }</script>
            </body>
            </html>
            
            
            
            
            
            ————————————————
            版權聲明:本文為CSDN博主「Regino」的原創文章,遵循 CC 4.0 BY-SA 版權協議,轉載請附上原文出處鏈接及本聲明。
            原文鏈接:https://blog.csdn.net/Regino/article/details/105321573
            

          原文鏈接:https://blog.csdn.net/Regino/article/details/105321573 





          產品簡化改版怎么做?

          資深UI設計者

          編者按:這篇文章來自 Taras Bakusevych 的文章《How to simplify your design》

          如今的數字產品,在不斷迭代過程中,會加入更多功能,添加更多的技術支持,更新進階的特性。而另一方面,企業對于構建簡單實用的產品,也同樣非常著迷。這當中當中毫無疑問是存在微妙的對抗和沖突的——如何讓更好更多的信息,以更加輕量易用的方式呈現出來?這就涉及到產品的簡化改版的技巧了。

          到底怎么算是簡單?

          讓產品變簡單,其實一件困難的事情。

          我們可以將「簡單」定義為「更加易于理解或完成」,或者是「不困難」。值得一提的是,簡單與否,它是主觀的,對于一個人而言簡單,對于另一個人而言不一定簡單。通常,我們是通過下面的三個步驟來判斷一件事情是簡單還是困難:

          在《簡單法則》當中,John Maeda 提供了10條法則,這些法則能夠幫你平衡商業、技術、設計所帶來的復雜性——更通俗的來說,就是如何合理簡化但是又能收獲更多。

          The Laws of Simplicity, John Maeda

          Meada,作為麻省理工多媒體實驗室的教授,他也是舉世聞名的平面設計師。在書中,他非常慎重地探討了「改進」這一概念,在他看來,它通常并不總意味著「更多」。無論是對于復雜度,還是簡化,都只是相對概念。結合合理的培訓,培養制造火箭的科學家也不是難事,但是總會有一些關鍵的因素,會讓簡單的事情變復雜,在設計中我們應該盡量去規避它們:

          那么,我們在具體的產品改版中,要如何合理地簡化呢?下面,是我結合這些原則來總結的21條建議:

          1、創造有焦點價值的產品

          有太多的產品試圖讓你更用戶做更多的事情,每個企業都試圖成為行業中的瑞士軍刀。但是,如果你希望產更加簡單,那么你需要有一個核心價值,并且確定產品真正針對的是誰,并非每個產品都應該成為 Facebook。你的產品的核心價值是什么?

          2、刪除所有不必要的內容

          想要簡化產品,最簡單的方法,是經過深思熟慮之后再進行簡化和刪除。如果組件或者模塊的功能、價值存疑,那么請刪除。次要信息、不常用的控件、分散注意力的樣式,都在這個范疇內。就這么簡單。一旦采用這一的方式,你會立刻馬上看到結果。當然,刪除的時候,請務必深思熟慮。

          「簡單并非完全沒有混亂,因為雜亂必然會伴隨簡化而存在。簡單本質上是對產品的定位和目的有更精準的描述。而沒有雜亂僅僅意味著這是一件不雜亂的產品,但是這并非簡單?!?

          ——喬納森·艾維

          3、將數據轉換為有意義的樣式

          我們日常設計的絕大多數產品,都涉及到需要用戶理解大量數據。當用戶對于趨勢和變化感興趣的時候,請盡量使用可視化的方式來呈現信息,而不是一堆數字。嘗試從數據中提取有效的內容,可視化地呈現在用戶眼前,這才是有意義的簡化。

          4、提供對快速決策的支持

          用戶長期遭受復雜決策機制的折磨,幫助用戶看清各種信息,更好的決策是簡化的方向之一。席克定律曾經對此作出過非常經典的解釋:用戶做選擇所需要花費的時間和精力往往會隨著選項數量的增加而增加。因此,如果你希望用戶決策變得簡單,那么你需要簡化選擇,消除不必要的選項,來幫助用戶進行選擇。

          5、太多選擇會嚇走用戶

          當前心理學理論和調研肯定了一件事,就是足夠多的選擇可能會給人帶來積極的情緒,相應的衍生出一個流行的觀點,就是選擇越多越好,但是人類天性中的管理能力是不足以支撐過多選擇的。

          果醬實驗是消費心理學當中最著名的實驗之一:為消費者提供更少的選擇,對于銷售更加有利。這一點是至關重要的——更少的選擇,能夠帶來更多的銷量。

          在這個實驗當中,選擇較少所帶來的轉化率幾乎是更多選擇的轉化率的10倍。這是一個選擇過載帶來不良后果的一個重要范例——過多選擇抑制了用戶選擇購買的想法。

          6、在提供多種選擇的時候,給用戶以建議

          如果多個選擇總是無法避免,那么不妨給用戶提供建議,或者告訴用戶多數用戶的選擇。向用戶清楚地傳達選項之間的差別,這在定價策略上會帶來更多積極的效果。

          7、將用戶注意力吸引到對的區域

          當你了解用戶流程的時候,就應該有針對性將用戶注意力引導到的對的目標上去,找到關鍵區域,讓用戶在界面中優先注意到它們。

          8、使用色彩和版式來呈現內容的結構層次

          你應該經常會聽到類似「可讀性很差」這樣的表述。有很多設計的確存在這樣的問題。在設計當中,有太多的因素會影響信息的傳達——字體的選取,大小變化、間距、大小寫和配色,都會影響到層次結構,影響到用戶汲取信息的方式。使用正確的配色和版式,讓內容的層次結構更加清晰,最好還能反映出品牌特征,這樣就更能增強吸引力和用戶對它的印象了。

          9、優化組織結構,便于管理

          下面兩張圖當中,要你數清楚有多少個圓點,哪一張圖會讓你這個過程更快?

          如同你所看到的,無序的點狀方陣和有序的是截然不同的,后者的認知負擔更低,識別速度更快。面對無序的信息和內容,我們需要逐個計數,但是面對有序的信息,則截然不同。

          有組織的元素不僅有著更高的識別度,也更容易被記住。在數字產品當中,記住常見控件的位置和功能無疑是重要的。同樣是上面這張圖,如果不是計數,而是記住每個點的位置,你能不能做到?毫無疑問,缺少組織結構,這是一件艱難的事情。

          10、給相關內容分組

          在簡化復雜頁面結構的時候,對組件和內容進行分組通常是最為有效的方法之一。通過層級劃分,用戶每次需要處理的信息量都會更小,而不是在無關的組件中來回穿梭尋找。借助邊框和卡片將相關度更高的內容整合到一起,這是非常便捷的方法。此外,關于關于分組,格式塔原理中的分組原則是非常值得借鑒的:接近性、相似性、連續性、閉合性和連通性。

          11、拆分任務,按列布局

          幾乎任何產品當中都會涉及到不同類別的表單,這是獲取用戶信息的一種方式。有的時候,即使刪除掉一些不必要的字段,表單依然會很長。因此,我們可以將巨大的表單或者任務拆分開來,這樣一來,整個執行過程會變得簡單不少。

          設計表單的時候,盡量使用單列而非多列,這樣更加順暢、易于填寫。用戶無需思考接下來要填寫什么,而是按照一條線繼續操作下去即可。

          12、透明清晰地展現路徑和狀態

          不確定性讓人感到焦慮,盡可能地在設計當中規避這種不確定性。這就是為什么要讓用戶在任何時候都清晰地知道他在流程中所處的位置,以及接下來會發生什么。總結之前所提供的信息也是個好主意,能夠減輕用戶的負擔,避免反復檢查之前的內容。

          13、替用戶進行計算

          人腦對于涉及計算的部分通常會比較費力,對于這些不太占用計算力的內容可以交由系統來進行計算,和計算相比,人的大腦在處理具象化的信息的時候更加擅長。嘗試利用系統,而不是將壓力轉嫁給用戶。

          14、用逐步展現來隱藏復雜性

          漸進式的展開是用戶體驗設計當中的一種范式,這種方法能夠讓界面更容易被理解。當涉及到多屏、大量信息和操作的時候,這種逐步展現的機制,可以避免讓用戶感到不知所措,也可以隱藏一些無關的信息,直到最后用戶可以清晰明白內在的關聯性為止。逐漸展現的方法,遵循著「從抽象到具體」這樣的一種概念,而 iOS 系統當中,所采用的導航方式,無疑就是這種思路的典范。一屏一個步驟,逐漸展現,最后給用戶一個確切的結果。

          15、善用通用的交互模式和設計范式

          用戶將絕大多數的精力都耗費在各種各樣的其他數字產品上,這意味著用戶對于其他的網站和APP的交互模式、使用方法都有著清晰的了解,對于特定的模式有具體的預期。因此,無論你是設計網站,某個 APP ,甚至是冰箱上的控制系統,都可以遵循既有的設計和交互模式。這并不是意味著停止創新,而是在很多事情上,你無需重新造輪子。

          16、初次體驗應當精簡

          在設計任何產品的時候,都應該讓用戶盡快感知到產品的價值。因此,除非是滿足功能性的需求,在用戶初次打開應用的時候,都盡量把其他的干擾去掉,因為這都是讓用戶了解產品價值的障礙。第一印象很重要,如果不滿意,很多用戶會立即離開。

          如果你第一次嘗試,即使是最簡單的操作,可能也是有挑戰性的,有時候新手使用 APP 的時候需要引導,但是最好的方法是盡量讓體驗和功能一目了然,搭配上下文環境的說明引導,而不是提供復雜繁瑣的學習材料。

          17、結合使用場景,巧用人體工程學

          簡約易用的產品大多能夠合理地結合人體工程學的設計。絕大多數人可能會想到諸如汽車座椅、控制面板和手柄這樣的案例,但實際其實遠遠不止。人體工程學適用于幾乎所有涉及人的產品設計。

          1954 年,心理學家 Paul Fitts 仔細了解了人的運動系統,并且提出了費茨定律——移動到目標所需要的時間取決于和目標之間的距離,并且和目標大小成反比。這個定律反饋到具體的界面設計上,就是盡可能讓常用元素更大,并放置到更容易被觸及的地方。

          18、提供直接編輯功能和推薦參數

          刪除不必要的交互組件、視圖或者是步驟,是簡化過程中必不可少的部分呢。用戶應該可以以最快的速度進行操作,比如在表單中直接操作,而不是在彈出框中進行編輯,這個被稱為「流程狀態」,「流程狀態」不應當被彈出框打破。此外,對于很多需要填寫的部分,最好提供參數推薦建議功能,就像搜索的實時推薦,讓用戶可以更為準確地輸入。

          19、使用智能默認值,減少認知負荷

          智能默認值——或者說智能占位符是一個非常有用的策略。一方面可以幫助用戶更快更精準的填寫表單,另一方面,也給用戶提供了相對準確的范例。只不過,默認參數的確定,需要設計師和開發團隊對用戶的使用場景等信息進行研究,通過測試和調研,確定用戶使用狀況,以此來確定默認參數應該是多少。如果需要明確地參數或者選項,那么可以將默認值設置為 90% 用戶可能會選的選項,并輔以說明。

          20、防止出錯

          出錯信息會給用戶帶來很大的壓力,為了避免用戶陷入這樣的狀況,防止用戶無法完成任務,請使用數據輸入檢查的功能,對于格式輸入錯誤的表單和內容,對用戶予以警報和提醒,避免錯誤發生。對于極為重要的操作,在用戶提交之前,讓用戶二次確認信息,做好檢查。對于某些強制性、破壞性乃至于不可恢復的操作,確保用戶知道這一操作帶來的影響。

          21、無障礙設計

          數字產品的可訪問性是老話題了,要讓產品和設計對于所有人——包括有視覺障礙的用戶,都可以輕松地使用。最常見的,就是不要使用色彩來作為傳達信息的唯一途徑,確保文本和背景之間有足夠的對比度,支持鍵盤導航操作等等??稍L問性問題并不限于特定的群體,堅持不斷的改善和提升,有助于每一個用戶的體驗。

          結語

          簡化并創造易于理解的產品并不容易,但是這是一條幾乎任何產品都要走的必經之路,簡化的方法和技巧有很多,雖然零碎,但是它們最終會帶著產品走向一條更好的道路。

          文章來源:優設    作者:Taras Bakusevych

          這份上萬字的指南,幫你學會用柵格系統構建響應式設計

          資深UI設計者

          今天,90% 的媒體互動都是基于屏幕的,通過手機,平板,筆記本電腦,電視和智能手表來與外界產生聯系。多屏設計已成為商業設計中不可或缺的一部分,響應式設計正迅速成為常態。作為 UI 設計師,我們希望為我們的產品在不同尺寸下都能為用戶提供良好的用戶體驗,柵格系統可以幫助我們做到這一點。

          即使是我們只針對一個尺寸進行設計,我們也經常面臨設計布局方面的問題。合理運用柵格系統可以幫助我們控制布局結構并實現一致和有組織的設計。柵格系統就像無形的膠水一樣凝聚一個設計,即使元素看上去是彼此分離,但通過網格將它們連接在一起,實現良好的層次結構,位置關系和一致性。

          設計師和開發者之間的協作過程中,柵格系統在前端開發中是被應用的很廣泛一套體系,許多優秀的設計都使用了柵格系統,使用柵格系統可以加速開發并保證視覺還原。柵格系統雖然是傳統設計方法中的一部分,但它仍舊能幫助我們去設計這個多終端的世界??吹竭@里,你可能非常想知道柵格系統在頁面中是如何運作的,那么今天我們一起來學習并且實踐我們的格柵系統。

          「The grid system is an aid, not a guarantee. It permits a number of possible uses and each designer can look for a solution appropriate to his personal style. But one must learn how to use the grid; it is an art that requires practice.」

          「柵格系統可以幫助我們設計,但卻不能保證我們的設計。它有多種可能的用途,并且每個設計師都可以尋找適合其個人風格的解決方案。但是必須學習如何使用網格。這是一門需要實踐的藝術?!?

          ——Josef Müller-Brockmann《平面設計中的網格系統》作者

          什么是柵格系統?

          柵格系統可以讓你依靠秩序與邏輯去完成設計。

          早在 20 世紀初,德國、荷蘭、瑞士等國的平面設計師們發現通過維持視覺秩序,從而使版面能更加清晰有效地傳遞信息,二戰后這種理念在瑞士得到了良好的發展,直到 20 世紀 40 年代后期,第一次出現了使用網格進行輔助設計的印刷作品。由瑞士設計師大師 Josef Müller-Brockmann(約瑟夫·米勒-布羅克曼)所著的《平面設計中的網格系統》一書,自 1961 年出版以來暢銷至今,對設計界有著深遠的影響。史稱 Swiss Typography Movement (瑞士新浪潮平面設計運動),后來成為全球風靡的 International Typographic Style (國際主義設計風格))。

          △ 約瑟夫·米勒一布羅克曼 (Josef Muller-brockmann, 1914-1996)

          瑞士的一位平面設計師和教師。1958 年任《新平面設計》(New Graphic Design)主編 1966 年被任命為 IBM 的歐洲設計顧問。布羅克曼因他的極簡主義設計與簡潔的排版、圖形和色彩而聞名,他的設計對 21 世紀的眾多平面設計師都產生了重大影響。

          柵格系統的優勢

          1. 減少決策成本提高設計理解力

          柵格系統在頁面排版布局、尺寸設定方面給了設計者直觀的參考,它讓頁面設計變得有規律,從而減少了設計決策成本;UI 設計也是需要理性的、客觀的、具有數學邏輯美感的。熟練運用網格系統能夠讓你的設計更有秩序和節奏感,頁面信息的展現更加清晰,提高閱讀效率,從而提供給用戶舒適的使用體驗。加快認知速度。這意味著用戶在使用產品完成特定的任務時,例如發送消息,預訂酒店房間或乘車。用戶能夠連貫地理解并找到下一條信息或下一步要采取的步驟。

          2. 響應化

          因為人們使用不同類型的設備與產品進行互動,從智能手表的小屏幕到超寬屏電視,交互是流暢的,并且沒有固定的尺寸。使用產品時,人們通常會在多個設備之間切換,以完成該產品的單個任務。所以響應式設計不應該是一種品,而是一種必需品。這意味著設計師不能再為單個設備的屏幕構建。多設備環境迫使設計人員根據動態網格系統進行思考,而不是固定寬度。使用網格可以跨不同屏幕尺寸的多個設備創建連貫的體驗。

          3. 加速團隊協作設計

          當多位設計師共同設計產品時,一個統一標準就變得尤為重要。如果沒有一個統一的框架去約束的話,我們的產品的頁面和組件的標準可能各式各樣,這樣的話整個產品的頁面都會比較混亂。因此,網格系統有助于將界面設計工作分開,因為多位設計師可以在統一的布局下進行不同部分工作,并且無縫集成并保持連貫。

          4. 加速開發并保證視覺還原

          大多數設計項目的實施,涉及到設計者和開發者之間的協作。柵格化提高了頁面布局的一致性和復用性;避免了設計師與開發者在細節上的反復溝通確認,從而提升了整個設計開發流程的效率、并能幫助開發者實現較為理想的設計還原。

          柵格系統的基本構成

          1. 列和槽(Columns and Gutters)

          列(Columns) 和槽(Gutters)。列(Column)是內容的容器,水槽(Gutter)用來調節相鄰兩個列的間距,把控頁面留白;列和列間距加上頁面邊距(Margin)加起來屏幕的水平寬度。列和列間距的內容區域(Content width)由 N個列和(N-1)個水槽組成。通常情況下,web 端采用 12 列,平板采用 8 列,手機采用 4 列。當然,你可以根據項目特點來設計你的網格系統,列和水槽的寬度我們可以利用 8 點網格系統來定義,下面會講到。列的數量越多,頁面就會被分割得越「碎」,在頁面設計時就會越難把控,適用于業務信息量大、信息分組較多、單個盒子內信息體積較小的頁面設計,列間距寬度數值對頁面的影響,與外邊距大體類似,即間距越大頁面越輕松簡單,反之亦然。用戶已經習慣通過鼠標滾輪或滾動條(scrollbar)來縱向瀏覽頁面內容,因此豎直方向可以無限延伸,所以柵格系統在豎直方向的柵格可以不體現出來,我們在執行設計時只要在水平方向保持規律的變化就可以了。

          2. 頁面邊距(Side Margins)

          頁面邊距就是內容區域(Content field)以外的空間,比較推薦的設計就是頁面邊距可以隨著屏幕尺寸的增大而增大。頁面邊距在移動設備上通常是 12Px到 40Px 之間,在平板設備和桌面設備頁面邊距變化就相當多了。在響應式設計中,你選擇了一個頁面邊距之后,縮小頁面寬度時頁面還是會有你設置的最小頁面邊距,直到到達下一個響應點(breakpoint)。當你增大頁面寬度時,頁面就有更多的頁面邊距,直到頁面寬度到達下一個響應點(breakpoint)。

          3. 模塊(Field Elements)

          模塊就是你的設計區塊,可以是一段文字,一張圖片,或是其他更加豐富的元素。背景元素并不能算作是設計模塊,所以并不需要遵循柵格系統。模塊的定義是很靈活的,它可以是個小的單位或是元素,也可以是一個元素豐富的區塊。

          以 12 柵格系統為例,一個 12 柵格系統可以根據業務需要被 2 等分、3 等分、4 等分、6 等分、12 等分,還可以被 1:1:1、1:2:1、1:3:2、2:3:3、1:2、1:3、1:5、3:5 等不對稱分割,具體采用哪種比例的組合需要我們根據自己業務需求來定。

          4. 8 點網格(8pt spatial system)

          柵格系統大的層面可以幫助設計者更好的進行版式設計與內容布局,而小的方面可以輔助設計師規范頁面內各種元素的對齊與間距的設定。從用戶體驗角度來講,這兩者同等重要,從執行層面來講,我們一般先做版式設計與布局,然后再填充內容、調整細節。

          由于列跟水槽的寬度是以網格作為基本單位來增加或者減小,所以柵格化的重要一步就是需要先定義好柵格的原子單位「網格」的大小。目前最普適易用的就是 8 點網格。我們也可以利用 8 點網格法來制定產品中的間距,建立 8 點為一個單位的網格,使用 8 的倍數來定義模塊的間距與元素的尺寸。8 點網格有如下幾點優勢:

          • 目前主流桌面設備的屏幕分辨率在豎直與水平方向基本都可以被 8 整除,使用 8 作為最小原子足夠普適??梢源_保不同布局之間的視覺一致性,同時可以靈活的適配多種尺寸的設計。以 8 為單位符合「偶數原則」。偶數原則可以在頁面縮放中的避免類似于 0.5、0.75、1.25 等次像素的出現,從而使頁面各類元素在大多數場景下都能有比較精致的細節表現。
          • 在網格系統中應該更加注重的是間距,而間距要遵循網格系統(例如使用 4、8、16、24、32 等和 8 具有規律的數字)同時在產品中的各類元素也要遵循這類原則(例如圖標大小、組件大小等)。所以布局的水平和垂直節奏和各個組件的節奏會相互重疊,整體的設計將更加完整。
          • 開發工程師使用的前端開源組件庫比如 Metronic、Antdesign 等也是基于 8 的原子單位來設計,因此如果設計師也使用以 8 為基本單位的柵格系統,開發與設計師相互對接就會更加方便,開發實現頁面時也能更高品質地去還原我們的設計。

          如果設計上沒有立即可識別的間距系統時,這種設計可能會讓用戶感覺廉價、不一致,而且通常不值得信任。如果設計上遵循一個 8pt 網格系統時,節奏變得可預測和視覺上的愉悅。對于用戶來說,這種體驗是經過修飾和可預測的,這增加了用戶對品牌的信任和喜愛。

          無論有多少個設計師在協同合作,現在都有一個一致的間距規范,決策成本將大大降低。設計師可以輕松地從另一個設計師停止的地方開始設計,或者輕松地并行構建。我們定義下規范可以及時和開發同學溝通,因此可以為工程師節省時間。

          5. 基線網格(Baseline Grid)

          基線網格由密集的水平行組成,這些行提供文本的對齊和間距準則,類似于您在直紋紙上書寫的方式。在下面的示例中,每 8px 行在紅色和白色之間交替。

          △ 基線網格

          提示:將所有行高設置為基本單位(8x 或 4px)的增量非常重要,這樣您的文本才能與基線網格完美對齊。

          △ 字體行高

          響應式設計

          1. 什么是響應式?

          設計師需要通過設計讓內容在不同的平臺上體驗最大化,確保讓用戶在任何一個屏幕上看到內容的時候,會覺得這些內容就是為這個平臺而設計的,而不是單純的縮放而來。這種無縫的體驗,才是跨屏幕設計的真正難點所在。想要制定一套針對不同設備和屏幕的設計方案,你需要一整套的策略。用戶體驗同時包含了性能、交互、效率等多方面內容,也就是說,對于一個線上的響應式頁面,我們不僅要關注視覺上看到的,也要關注我們操作、使用時的感受,這些綜合因素最終影響著用戶使用時的效率與體驗。

          2. 響應式設計的核心步驟

          確保核心的用戶體驗

          雖然用戶體驗是無處不在的,但是對于特定產品,最核心的體驗是存在的。產品通常是用來解決用戶所面臨的特定問題的,它的這一特質讓產品變得有意義。關鍵的內容和關鍵的功能的組合,通常構成了產品的核心用戶體驗。如果你并沒有想明白這個問題,不妨問問自己:用戶需要完成哪些最常見/最重要的任務?找到問題的答案之后,你的產品就應當從各個方面、各個渠道,完整而全面地支撐這些功能,幫助用戶完成這些任務。舉個例子,Uber 的核心用戶體驗是隨時隨地叫車,無論設備的屏幕大小如何,你進行的設計全部都應該圍繞著這個需求和功能來進行。叫車是 Uber 的核心功能,即使使用 Apple Watch 這種極小的屏幕尺寸都應該順利地完成這個任務。

          敲定你的產品所覆蓋的設備類型

          現在的移動端設備屏幕尺寸各不相同,單獨為某一個設備設計內容無疑是不現實的。根據你的產品覆蓋人群、受眾分類、使用場景,綜合考慮你的內容會優先呈現在哪些設備和平臺上,然后有意識地篩選出常見的設備類型:手機,平板,桌面端,智能電視,智能手表……

          不同的設備組合通常是基于不同的場景、需求和服務來構成的,用戶會針對不同的屏幕進行不同模式的交互,甚至處理的內容也會有差異。比如說,在手機上,用戶更加傾向于使用輕量級的任務,并且進行一定量的溝通和交流。在平板上,用戶行為更多集中在內容消費上,并且目前平板的使用量被認為在逐步降低。桌面端依然是用戶完成較為專業、復雜任務的首選平臺,足以應付復雜多樣的內容。了解各種設備類型和使用場景是用來構建用戶體驗的關鍵。

          針對不同內容來匹配用戶體驗

          并非所有的內容都符合不同設備的使用場景,比如智能手表就不適合展示大量的文本內容。你的產品所覆蓋的設備組當中,每種設備的使用場景不同,應該匹配的用戶體驗也不一樣。移動端用戶和桌面端用戶的需求就是不同的,場景差異也很大。以 Evernote 為例,它可以在多種不同類型的設備之間同步和切換,其桌面端版本就針對用戶的內容需求進行了優化:Evernote 的桌面端應用程序針對閱讀性的內容和多媒體進行了優化,而移動端的 Evernote 則強化了拍攝記錄、圖片和音頻記錄的功能:其次,不同的設備屏幕具備不同的輸入方式,設計師如果忽略輸入方式上的獨特性,也常常會出現許多問題,這里就不擴展開來了。

          優先為最小的屏幕做設計

          一直以來,設計師都習慣從最大的屏幕著手設計,最后考慮最小的屏幕上的顯示效果,這意味著絕大多數的設計都是從桌面端開始設計的,通常桌面端的內容和功能更全面。當桌面端的整體設計完成之后,再推進到其他設備端的設計。然而,在進行桌面端設計的時候,我們常常會遭遇「廚房水槽」困境:由于產品通常會牽涉到多個利益相關方,許多多余的功能會被加入進來。而實踐經驗表明,移動端優先的設計往往能夠更好的專注于核心功能,更適合作為產品設計的起點。當你優先設計最小屏幕所需要的界面的時候,這種局面會強制你從最關鍵最重要的地方開始設計。這也是之前設計圈和產品開發領域一直所強調的「移動端優先」的策略的由來。在此之后,再進行平板、桌面和電視端的設計,就是一個自然地做加法的過程了。在絕大多數的案例當中,最小屏幕通常是手機屏幕。

          測試你的設計

          產品的測試環境并不一定都得是在現實世界中尋找,但是在盡可能讓真實的用戶來做可用性測試,并且在產品發布之前解決所有的用戶體驗上的問題。

          3. 為何要利用柵格系統來進行響應式設計?

          響應式可以響應的前提有兩點:1、頁面布局具有規律性、2、元素寬高可用百分比代替固定數值,而這兩點正是柵格系統本身就具有的典型特點,所以利用柵格系統進行響應式的設計是順理成章的,也比較快捷,所以響應式與柵格化天生一對好搭檔。

          如何建立柵格系統

          第1步:確定列的數量

          第一階段先不要限制自己的列數。首先,創建一個低保真或高保真的原型。設計一些基本元素和用戶流程。在此之后,就開始設計最優的列數和大小。如果在項目開始設計之后不得不改變我們的柵格系統,不要有負擔,我們需要有一些試錯的空間。

          我們在設計頁面時,用到最多的布局方式就是等分布局,即頁面內容區域被 N 等分,每一份的寬度則根據屏幕寬度自適應調整。那么就從這個角度出發,思考一下頁面的網格應該設置為多少列,才能的滿足各種等分布局的需要。與 web 類似,移動端最方便的網格之一是 12 列網格。這個網格將允許我們在一行中同時放置偶數和奇數個元素。

          對于移動端來說,12 列網格的缺點是一個列的寬度太小,你可能很少創建一個列寬度的元素。如果你選擇 2、4 或 8 列網格,請記住在一行中放置奇數個元素可能會出現的問題。

          Pro-Tip:

          界面設計通常包含數百個不同的頁面,因此,一個網格可能不適合所有的頁面。如果需要,創建額外的柵格系統,但不要忘記設計的一致性。網格系統的一致性:相同的布局邊距、列之間相等或成比例的水槽,以及更改列本身的寬度時其他模塊也需要保持相同的比例。

          第2步:定義水槽和邊距

          首先,讓我們先翻閱目標屏幕的設計 Guideline,以找出通常頁面邊距(Side Margins)。目前,Android 和 iOs 的最小推薦布局邊距為 16pt。web 端則依照屏幕尺寸不同而不同。這意味著,如果你希望遵循系統指南,則頁面邊距不應小于 16pt。(但可以更大的)

          在選擇 12 列網格時,列之間的水槽不應該太大,因為由于列的寬度小和它們之間的大寬度的水槽,列將在視覺上產生分裂的感覺。同時我建議你選擇與8pt 間距系統成比例的水槽大小。所以布局的水平和垂直節奏會相互重疊。水槽與頁面邊距成比例。那么網格更加一致,也將允許我們輕松地在其中放置特殊元素,如輪播(carousel)。

          第3步:定義 8pt間距系統

          了幫助不同設計能力的設計者們在界面布局上的一致性和韻律感,統一設計到開發的布局語言,減少還原損耗。在大量的實踐中,我們提取了一組可以用于 UI 布局空間決策的數組,他們都保持了 8 倍數的原則、具備動態的韻律感。經過驗證,可以在一定程度上幫助我們更快更好地實現布局空間上的設計決策。定義網格系統方法很多,如運用 8 點網格系統、斐波那契數列、某最小原子單位的增量、從底層系統參數化定義間距等,我們以最小原子單位的增量為例去定義網格系統。最小單元格的數值選擇需要從兩方面考慮:

          • 一方面是該數值是否能被大多數手機屏幕的寬度整除,即廣泛的適用性;
          • 另一方面是在具體使用時是否具有一定的靈活性。

          在適用性方面,4、6、8、10 這四個數值都是基本可以滿足的,在靈活性方面,4px 表現最佳,但是頁面就會被分割的非常細碎,在設計時比較難于把控。因此我們需要根據 APP 的實際情況選擇合適的數值,4px 或 6px 單元格比較適合頁面內容信息較多,布局排版比較復雜的產品。而 8px 單元格對一般的設計場景都可以很好的滿足,比較適合大多數的 項目,因此是比較推薦使用的。

          那么假設我們以 8 為基準的去延展系統間距,得到如下間距系統:

          1、2、8、16、24、32、40、48、56、64、72、80、88、96、192 等,這里都是 8 的倍數或能被 8 整除

          但是目前間距數量太多,過于細碎也會導致間距比較亂,所以我們繼續優化梳理(以 6 為基準,前面個數是后面個數的 2 倍遞增),得到以下間距系統:

          1、2、8、16、24、32、48、64、80、96

          第4步:sketch布局設置

          利用 sketch 的布局設置功能,即可快速搭建出網格系統的參考布局,在平時做設計的過程中,可以經常使用 Ctrl+L 快捷鍵切換布局的顯示,提高設計效率。

          我們來解釋一下這些設置分別是什么:

          • Total Width:就是內容區域(Container)的值;
          • Offset:表示柵格的偏移量,我們只要設定完成以后按 Center 按鈕即可,會自動居中;
          • Number of Columns:就是柵格數;
          • Gutter on outside:是非常重要的設置,勾選以后才能跟前端的柵格算法匹配;
          • Gutter Width:就是柵格之間的間距;
          • Columns Width:就是柵格的寬度。

          如何做到響應式?

          在傳統的柵格化系統設計中,列的寬度和水槽的寬度是保持不變的,只是列的「數量」發生變化。為什么要這么處理呢?這是為了讓設計更簡單。如果一組三張卡片分別放在桌面的四列上,那么在平板電腦上,會顯示兩張卡片,并把第三張卡片進行折行顯示在第二行上。不需要做任何的調整,因為已經知道它位于第四列上了。在手機上,答案也很簡單,只需要一張卡片,其他的就會自動堆到下面的行中。但是目前我有更多的響應策略,例如當視窗(Viewport)發生變化時,內容區域的元素如何去響應,具體到我們當前的柵格系統,就是 Columns、Gutters、Margins 以及由 Columns 跟 Gutter 組成的盒子(BOX)四者的值(主要是寬度)如何變化,以及在這種變化之下我們頁面的布局如何調整。

          1. 固定柵格或是斷點系統(Fixed boxes or breakpoint system)

          固定網格,列寬和水槽寬不會改變,只是改變列的數目,當窗口縮放時,排版布局不會發生任何改變,只有當達到一個臨界值(開發那邊設置好的固定的值),界面才會發生改變。在此之前界面排版都是不變的,就像一部分被切掉了。

          如果開發那邊寫了一個固定柵格,當你從桌面縮小到平板電腦,就像是在桌面的瀏覽器寬度時,你不會看到任何變化,設計就像是被剪掉了一樣。但當達到平板屏幕尺寸臨界點時,設計布局馬上就會改變,平板電腦上的顯示效果就會好起來。如果繼續減小這個值,同樣的事情也會發生,在到達另一個臨界值之前,設計看起來都是不變的。下面是常見的斷點系統(Breakpoint System)

          如圖,響應式是以視窗的最小寬度作為基本依據來制定每種寬度下 Columns、Gutters、與 Margins 的響應策略,也就是說 Viewport Min-width 是做出響應的觸發條件,視窗每達到一個最小寬度,就會觸發該寬度下預設的頁面布局方式,而每種布局都是在該寬度下的最佳布局,也是因此,響應式才會在各種復雜分辨率條件下都能給用戶比較好的體驗。
          每個視窗寬度的最小值是觸發響應的關鍵值,因此我們給這些用于觸發的關鍵值起了個名字叫「Breakpoint」,每個 Breakpoint 觸發一種響應策略。

          2. 流動柵格(Fluid Grid)

          流動柵格系統是編輯內容,儀表板,圖像,視頻,數據可視化等理想的響應策略。當窗口縮小時,內容將動態地發生變化,文本會進行換行,元素也會變窄。然而,這些元素在內容寬度縮小到下一個臨界值之前,布局是不會變化的。在各種情況下,對用戶來說,擴展內容的大小比擴展可見內容的數量更有用。

          所以我想說的是,斷點 BreakPoint 只是一個更改布局的參考點。這就是為什么列寬和水槽的數量不會改變的原因,因為我們想讓設計師在考慮布局時能夠更容易地創建一致性。內容寬度會隨著窗口的縮放而發生改變,例如圖片會縮小,文本會換行。水槽的寬度不一定是固定的,可以隨著頁面寬度變化。

          在每個斷點處,列計數是固定的,列寬度是最小網格 8PT 的倍數。行高是列大小的倍數,遵循推薦的縱橫比。邊距和填充是小單位的固定倍數。在斷點之間,實際列寬是網格區域的百分比,而不是一個小的單位倍數。內容尺度流暢。

          首先從所以屏幕大小中選擇一個基本尺寸,然后按照推薦的縱橫比以基本大小的倍數構建每個響應式尺寸。當每個塊使用相同基礎大小的倍數時,就會出現網格。遵循此方法可確保柵格系統一致性,甚至跨產品的一致性。

          3. 混合柵格(Hybrid Boxes)

          在實際項目中,使用流動網格和固定網格的組合也是常見的做法。網站通常是流動網格,因為它要去適應各種不同終端的大小。后臺系統設計、工具型的界面設計就比較經常使用網格和流動網格組合的形式。例如的后臺管理系統(dashboard)側邊欄是固定網格,右側內容是流動網格?;旌蠔鸥裨诿總€維度上有不同的縮放規則,所以它們不使用統一的縮放比。當用戶需要調整瀏覽器的大小以使內容在一個維度上伸縮而在另一個維度上不伸縮時,便使用混合網格。

          面板對柵格系統的影響

          1. 靈活面板(Flexible panels)

          靈活的面板允許折疊和擴展狀態。面板的展開狀態為固定寬度,用戶無法調節。當用戶將鼠標懸停在折疊的面板上時,面板就會展開。當靈活的面板擴展時,它們要么壓縮內容和網格,要么將內容推到瀏覽器邊緣之外。

          2. 固定面板(Fixed panels)

          固定面板保持靜態寬度,不能折疊,也存在于響應網格之外。

          3. 懸浮面板(Floating panels)

          此面板樣式漂浮在主要內容區域之上,不影響響應網格。浮動面板將任何 UI 元素隱藏在其下方,用戶必須將其移除。內聯菜單、下拉菜單和工具提示也是浮動的。

          總結

          寫這篇文章的目的是想提供一些關于如何在響應式設計中使用柵格系統,我知道對于我自己來說,我花了很多時間理解網格是如何工作的。我在 YouYube 上看了很多視頻,也閱讀了大量的文章,但每個人都在關注它為什么重要,卻不去注重到底怎么在自己的項目中使用這些原則。

          你要做的最好的事情就是從現在開始注意那些優秀設計是如何對齊元素的,你將會開始閱讀這些設計系統。為了幫助理解,這里有一些設計系統概述了它們的網格使用:

          在完全理解了網格的工作原理之后,你將成為了一名更好的設計師,因為你知道了你的設計將如何在臨界值之間進行轉換。你也可以落地你的設計,使它們能夠達到像素級完美。這樣的規范帶來了更一致,更簡潔的設計,當用戶從一個界面到另一個界面流轉時,這真的提升了產品的檔次。我建議在你的設計中去應用這些網格,并和開發同學一起,以實踐的方式將它們落地,這將會是一個非常不錯的進步。

          文章來源:優設    作者:IvanZheng

          免費可商用!這款中文楷體太適合做封面字了!

          資深UI設計者

          有時候做國風的設計作品,如果選擇黑體和宋體可能太過端正,但選擇書法字體的話,可能因為飛白或過度連筆導致字體不容易識別,那選什么字體呢?我建議大家選擇楷體,注意不是選系統自帶的楷體,而是今天推薦的演示悠然小楷,字體手稿由一位美麗女生書寫,其字體有著悠然自得、閑情逸致的氣質。

          「演示悠然小楷」是由 keynote 研究所 x 秋葉 PPT 聯合發布的一款免費商用字體,推薦設計師們收藏或下載。下載地址見文末。

          字體案例演示

          1. 青春/知識

          △ By@偉崇

          2. 文學/歷史

          △ By@偉崇

          3. 游戲/小說

          △ By@小敏

          4. 中式地產

          △ By@偉崇

          5. 商務風

          △ By@偉崇

          8. 主視覺設計

          △ By@畫生

          △ By@畫生

          9. 海報物料

          △ By@畫生

          △ By@畫生

          △ By@畫生

          10. 電商廣告

          △ By@畫生

          △ By@畫生

          △ By@畫生

          11.文字排版

          △ By@畫生

          △ By@畫生

          12. 品牌形象設計延展

          △ By@畫生

          △ By@畫生

          △ By@畫生

          字體搭配

          1. 思源宋體

          △ By@澄音

          2. 方正宋刻本秀楷

          清刻本悅宋用得多了,不如試試這款有些相似但又有不同的「方正宋刻本秀楷」,較之前者字形更為周正,筆畫更為果決,娟秀之中蘊藏力量。

          △ By@畫生

          3. Garamond

          Garamond 是一款古樸傳統的襯線字體,在西文體系中歷史悠久,恰因沒有特殊的個性而被廣泛使用,是老式襯線體中最具代表性的字體,與同樣以端正工整為名的楷書作搭,最能相互映襯。

          △ By@畫生

          4. 阿里巴巴普惠體

          基礎黑體的字體沒有復雜的修飾,進一步弱化次要信息,強烈對比下讓主角得以更好的突顯,也讓畫面層次感更加豐富,而基礎型的黑體,必然首推阿里巴巴普惠體。

          △ By@畫生

          字體下載

          • 字體名稱:演示悠然小楷字體
          • 系統名稱:slideyouran(有的軟件顯示不出中文字體,就用這個來搜索)
          • 網頁 CSS 字體屬性:font-family:slideyouran
          • 字體版權:永久免費,包括商用

          下載地址:https://pan.baidu.com/s/1ohOK2RSEA9vsfHAbfvpZmw 提取碼:ypae

          如何讓深色模式更精致?

          資深UI設計者

          通過一些案例進行比較與實驗,探索如何將 UI 深色模式設計得更好。

          iOS 作為 UI/UE 設計的風向標,一直是行業的引領者,不管你愿不愿意承不承認,他的每一次更新變化總能帶動 UI 設計行業的一些大大小小的變革,并且產生更多的追隨者,比如當年的 iOS7 開始的扁平化設計風格,對后續設計風格的影響直到現在已經 7 年了。

          在最近半年,iOS 在 UI 設計風格上最大的變革莫過于 DarkMode(深色模式),在 DarkMode 之前,我們熟悉的 UI 界面往往都是淺白色界面為主,而從 iOS13 開始正式使用了 DarkMode,界面突然可以變深色了,蘋果官方說這樣設計可以讓眼睛更舒服地長時間閱讀,為革命保護視力,而且更加省電增長續航,具體結果我們不得而知,需要科學家們去驗證了,但是對于我們設計師來說帶來的挑戰就是要「黑白無?!沽?。

          其實 DarkMode 從測試版算起已經差不多推出了有半年的時間了,一些知名的 APP 產品早已經有了自己的兼容方案,同時 iOS 原生界面也給了我們很多最佳實踐案例,按道理大家對于 DarkMode 的設計方式方法應該已經掌握了差不多了,但直到這幾天微信正式推出了自己的 DarkMode 兼容方案,才發現對 DarkMode 的探索還需要設計師們多多努力。謹以此文表達一下自己的觀點,不妥之處敬請海涵。

          從一個「列表頁面」說起

          列表視圖(TableView)是 iOS 中最常見的界面組件,一般常見于設置與欄目列表頁面:

          iOS 設置界面的淺色模式和深色模式看起來都非常協調。

          下面我們看看微信發現頁面,這個頁面和 iOS 設置是很相似的。

          如果單獨看微信發現頁面的淺色模式實際效果還是不錯的。

          但是直接轉換到深色模式下就感覺突然差的很多了,甚至可以說是有點難看,這次微信真的做了一次黑天鵝?

          到底是什么原因讓微信發現頁面在深色模式下視覺體驗如此之差呢?

          我們不妨將兩個功能布局都相似的深色進行放在一起進行一下比較:

          組成色彩分析:

          在色彩這塊在這兩個頁面中微信和 iOS 基本保持一致,四層灰度設計能更好的保持頁面整體干凈整潔且層次分明,但是在 A 背景色上,微信的背景色選擇了黑色偏綠的顏色,應該是微信設計師還是想體現出產品的標志色原色。

          文字的顏色也較 iOS 略微深一點,但是在整體上影響并不大。

          看來在主要色彩上并沒有什么問題,那么為什么微信這個界面與 iOS 界面比起來視覺上要感覺差一些呢?

          下面來看一下圖標

          圖標設計分析:

          圖標上的差別主要在于線寬與外框,微信采用無外框統一線寬的線形圖標,iOS 采用的是有外框剪影圖標。

          我們我們把圖標進行互換會怎么樣呢?

          觀察到了嗎?別看錯了!

          是的,我故意把位置做了對調,左邊是 iOS,右邊是微信。替換圖標后的微信明顯加分不少,整個界面都整齊多了,而 iOS 換了圖標后明顯變得不夠整齊了,潦草很多。

          那么結論是微信的無框線性圖標在深色模式下兼容有問題?是的,的確如此。但是等一下,還有一些細節你注意到嗎?換了圖標的微信界面和之前的 iOS 界面比起來明顯還是有點不夠整齊,為什么呢 ?

          我們回過頭來從細節再看一下 iOS 界面。

          我們按照這個思路把剛才微信替換圖標界面再排序一下!

          △ 界面視覺體驗明顯整齊了很多是不是!

          疑問:

          為什么細線圖標和無框圖標會在深色背景表現不夠好,而在淺色背景下就沒問題呢?

          是不是所有的 UI 都會存在這樣的問題呢?

          我們再來看一些例子:

          看來結論是一樣的,線性圖標在深色背景下的表現都是差強人意,反觀帶框圖標適應性很強,淺色和深色模式下均能良好的適配,我來分析一下原因。

          當年伽利略用望遠鏡往天上看,發現木星比金星大,換成肉眼看后金星則比木星大。他認為是眼睛的某種視覺特性造成了這種現象。

          德國物理學家赫爾曼把這種錯覺稱為輻照錯覺,就是說在黑暗背景下,亮度越高的物體看起來面積越大。

          再來看一張圖片

          哪個圓圈看起來更大,顯然是黑色背景下的白色圓形,實際上這只是一種錯覺,所有圓圈是一樣大。

          光亮刺激會使得神經元產生非線性放大作用,導致刺激比實物本身看起來更大,白色圓形更亮,所以看起來更大一些。

          線性圖標是用線條勾畫圖案達到隱喻效果,一般線粗是 2px~6px 像素。

          設計師在設計時都是以最終視覺作為參考,而設計稿本身多是淺色背景,所以在淺色背景的映襯下圖標視覺會顯得稍大,視覺基本是平衡的,假如設計是 4px 而呈現出的效果其實是 6px 左右。

          是不是覺得哪里有點不對了?按照這個邏輯黑色背景下白色線圖標不應該是視覺更大、更明顯嗎?

          我們還需要考慮一個因素,那就是色彩,之前的幾個界面案例的線性圖標都是彩色的,特別是黑色背景下,不同色彩的圖標放在一起,會有明顯的忽大忽小的感覺,會讓界面感覺非常凌亂。

          是不是感覺黃色最大,紅色的最小?但是其實是一樣的,這還是相同形狀的,要是圖標形狀不同感受會更明顯。

          看一個實際中的例子:

          由于都是單色線性圖標,在淺色和深色下表現還都不錯的,但是單色圖標略顯界面單調,并不太建議這么設計。

          毫無疑問,未來的 UI 場景需要適配多背景色風格,圖標除了具備好看隱喻之外,更需要具備抗干擾性。

          帶框圖標是一個不錯的解決方法,大膽預測帶框圖標會將成為未來一段時間圖標設計主流!

          結論

          • 深色模式中灰度色階在一個界面最多可分為四層。
          • 為了適配深色模式,今后有框圖標將會成為圖標設計風格主流。
          • 同樣為了適配深色模式,細線圖標將會被淘汰,剪影和粗線圖標會流行起來。

          • 圖標除了個體設計上用心,在排列上也會極大影響到頁面的整合視覺,光譜排列法是個不錯的選擇。

          • 文章來源:優設    作者:殘酷de樂章

          日歷

          鏈接

          個人資料

          藍藍設計的小編 http://www.syprn.cn

          存檔

          亚洲va欧美va天堂v国产综合