簡潔、高效、可自定義的開源的Android評論控件---CommentView

@TOCjava

目錄

1、控件概述

1.一、介紹

CommentView是一個簡潔、高效、可自定義的開源的Android評論控件,支持蓋樓評論回覆模式下拉刷新評論上拉加載更多評論加載更多回復等功能,可自定義(樣式、數據模型、佈局)、可輕鬆按需求擴展功能。android

1.二、功能特色

功能特色
支持下拉刷新評論
支持上拉加載更多評論
支持加載更多回復
支持蓋樓評論回覆模式
可設置空數據視圖、錯誤視圖
支持自定義數據模型
支持自定義樣式配置器
支持自定義佈局

1.三、效果圖

GIF壓縮後效果變差,想親自體驗能夠到下面的倉庫下載安裝包。

想要什麼的樣式效果能夠自定義實現git

默認樣式效果(GIF)github

默認樣式

自定義樣式效果(自定義仿微信公衆號評論)(GIF)算法

自定義樣式仿微信公衆號文章評論

1.四、源碼倉庫

倉庫 網址
碼雲 https://gitee.com/jidcoo/CommentView
GitHub https://github.com/Jidcoo/CommentView
Jcenter(Maven) https://bintray.com/longyun/maven/CommentView

2、框架類解析

2.一、項目目錄結構

項目結構圖
項目結構圖

2.二、項目總體框架

項目結構圖

CommentView只負責對外提供業務方法,全部的業務實現和核心代碼都在com.jidcoo.android.widget.commentview.operator包和com.jidcoo.android.widget.commentview.adapter包。com.jidcoo.android.widget.commentview.callback包是業務和控件的橋接包。

2.三、框架分解

2.3.一、數據模型類(Model)

總體框架
模型框架json

2.3.1.一、PagerEnable(分頁)
/**
 * 分頁模型<br>
 * <br>
 * 在Android端只實現爲對分頁數據的Get/Set操做<br>
 * <br>
 * 具體的分頁邏輯應該由後端完成,客戶端只負責對數據的處理<br>
 * <br>
 * @author Jidcoo
 */
public class PagerEnable {
    /**
     * 當前頁碼
     */
    public int currentPage;
    /**
     * 每一頁數據的大小
     */
    private int pageSize;
    /**
     * 總頁數
     */
    public int totalPages;
    /**
     * 數據總數
     */
    public int totalDataSize;
    /**
     * 下一個頁碼
     */
    public int nextPage;
    /**
     * 上一個頁碼
     */
    public int prefPage;
    //
    //省略大量Get()/Set() 方法
    //
}

PagerEnable類是一個分頁模型,是項目數據模型中很是重要的一個基類,評論數據抽象模型AbstractCommentModel、CommentEnable都繼承自PagerEnable類。在業務類的核心代碼中都須要用到PagerEnable類中的頁碼數據和方法。後端

在Android端不涉及具體分頁邏輯,具體分頁邏輯在後端完成。注意:在實際使用中,若是要實現分頁加載(更多評論數據、更多回複數據),那麼要展現的數據必須包含這些分頁數據字段。 好比在json數據中必須包含這些數據,分頁功能才能真正有效。也就是說不管你用什麼做爲數據,都必須在數據中包含相關分頁數據字段。繼承了PagerEnable類的全部類對於的數據都要包含相關分頁數據字段api

2.3.1.二、ReplyEnable(回覆數據基類)
/**
 * 自定義回覆模型必須繼承自此類
 * @author Jidcoo
 */
public class ReplyEnable { }

ReplyEnable類是回覆數據模型的基類,自定義回覆模型必須繼承自此類。能夠看出此類沒有任何的繼承關係緩存

可能有人會問,回覆數據不是須要分頁嗎?爲何ReplyEnable類沒有繼承自PagerEnable分頁模型類?沒有分頁數據回覆數據如何分頁?首先若是要實現回覆數據分頁加載,那麼回覆數據是須要分頁的。可是回覆數據的分頁數據應該屬於它所在評論(CommentEnable)的那一級中,而不是在回覆數據自己上,由於在分頁數據在回覆模型中沒有任何意義。是經過回覆數據所在的那個評論模型(CommentEnable)中表現回覆數據的分頁狀況微信

全部回覆數據的分頁加載都是基於它所在的評論模型所決定和控制的。而CommentEnable類正是繼承自PagerEnable類,繼承自PagerEnable類的惟一目的就是用來控制回覆數據的分頁功能。理解這段話就可以知道爲何ReplyEnable類只是一個空類了。

自定義回覆數據模型例子:

/**
 * 首先繼承自ReplyEnable類
 * 而後設置回覆模型中所須要的數據就能夠了
 */
public class Reply extends ReplyEnable {
      public String userName;
      public String reply;
      .....
      設置更多具體的屬性,好比時間、點贊數之類的數據
      .....
}
2.3.1.三、CommentEnable(評論數據抽象類)
/**
 * <p>自定義評論模型抽象類</p>
 * 自定義評論模型必須繼承自此抽象類
 * @author Jidcoo
 */
public abstract class CommentEnable extends PagerEnable{

    /**
     * 必須實現該方法並確保返回值非NULL
     * @return
     */
    public abstract <R extends ReplyEnable> List<R> getReplies();
}

CommentEnable是一個繼承自PagerEnable類的抽象類。繼承自PagerEnable類的惟一目的就是用來控制回覆數據的分頁功能,因此繼承自此類的實體類就具能夠支持所在評論的回覆數據的分頁加載。

自定義評論數據模型必須繼承自CommentEnable類並實現getReplies()抽象方法getReplies()方法很是重要,必須實現。該方法返回一個List<? extend ReplyEnable>的list,這個list的元素是繼承自ReplyEnable的自定義的回覆數據模型實體類。如第二點中的例子Reply類。

自定義評論數據模型例子:

/**
 * 首先繼承自CommentEnable類
 * 而後實現抽象方法getReplies(),返回一個回覆數據的List
 * 而後設置回覆模型中所須要的數據就能夠了
 */
public class Comment extends CommentEnable{
      public List<Reply> replies;
      public String userName;
      public String comment;
      .....
      設置更多具體的屬性,好比時間、點贊數之類的數據
      .....
      /*@Override
      public List<R> getReplies() {
            return null;
      }*/
      //必須實現getReplies()方法。
      @Override
      public  List<Reply> getReplies() {
            return replies;
      }
      /**
       * 首先繼承自ReplyEnable類
       * 而後設置回覆模型中所須要的數據就能夠了
       */
      public class Reply extends ReplyEnable {
            public String userName;
            public String reply;
            .....
            設置更多具體的屬性,好比時間、點贊數之類的數據
            .....
      }
}

注意:CommentEnable類中getReplies()的原型是這樣的:

@Override
public List<R> getReplies() {
            return null;
      }

因此實現getReplies()方法的時候只須要把泛型R改成本身定義的回覆數據模型類就能夠了,而後在方法中返回這個實體類的集合。

2.3.1.四、AbstractCommentModel(評論數據總抽象類)
/**
 * 數據抽象模型<p></p>
 * 自定義模型必須繼承此抽象模型,並實現其中的方法<p></p>
 * <u>注意:使用自定義數據類型就必須自定義佈局實現,不然會拋出數據模型的java.lang.ClassCastException異常</u><br></br>
 * AbstractCommentModel中傳入的泛型<C extends CommentEnable>必須繼承自CommentEnable,查看{@link CommentEnable}
 * @param <C> 自定義的評論數據模型
 * @author Jidcoo
 */
public  abstract class AbstractCommentModel <C extends CommentEnable> extends PagerEnable{
    /**
     * 必須實現該方法並確保返回值非NULL
     * @return
     */
    public abstract List<C> getComments() ;

}

AbstractCommentModel類是評論模型抽象類,繼承自PagerEnable類用來控制評論數據的分頁功能。同時它是一個泛型抽象類,泛型接受的類必須是繼承自CommentEnable的類,好比第三點中的例子Comment類。

一樣的,實現自定義的數據模型必須繼承自AbstractCommentModel類並實現getComments抽象方法(設計思想與CommentEnable類同樣)。getComments()方法很是重要,必須實現。該方法返回一個List<? extend CommentEnable>的list,這個list的元素是繼承自CommentEnable的自定義的評論數據模型實體類。如第三點中的例子Comment類。
注意:使用自定義數據類型就必須自定義佈局實現,不然會拋出數據模型的java.lang.ClassCastException異常。緣由能夠查看defaults包下的DefaultItemBuilder類對於默認佈局的實現方法。

AbstractCommentModel其實是業務方法中使用到的最外層的數據模型類了。查看CommentView的源碼中的業務方法就能夠知道,主要的有關數據的業務方法接受的參數都是AbstractCommentModel,也就是說接受繼承自此抽象類的數據模型。

自定義模型例子:

/**
 * 首先繼承自抽象泛型類AbstractCommentModel<C>
 * 其中泛型C用自定義的評論模型類替換就能夠了
 * 而後實現抽象方法getComments(),返回一個評論數據的List
 */
public class CommentModel extends AbstractCommentModel<CommentModel.Comment> {
    public List<Comment> comments;

    //必須實現getComments()方法並確保返回值非NULL
    @Override
    public List<Comment> getComments() {
        return comments;
    }

    /**
 * 首先繼承自CommentEnable類
 * 而後實現抽象方法getReplies(),返回一個回覆數據的List
 * 而後設置回覆模型中所須要的數據就能夠了
 */
public class Comment extends CommentEnable{
      public String userName;
      public String comment;
      public List<Reply> replies;

      /*@Override
      public List<R> getReplies() {
            return null;
      }*/
      @Override
      public  List<Reply> getReplies() {
            return replies;
      }
      /**
       * 首先繼承自ReplyEnable類
       * 而後設置回覆模型中所須要的數據就能夠了
       */
      public class Reply extends ReplyEnable {
            public String userName;
            public String reply;
      }
}
}

在自定義模型中要繼承自抽象泛型類AbstractCommentModel < C extends CommentEnable>

public class CommentModel extends AbstractCommentModel<C>
@Override
    public List<C> getComments() {
        return null;
    }

這裏的泛型C用自定義的評論數據模型類(如第三點的例子Comment類)替換就能夠了。

其實在自定義模型類中作的事情並很少,由於須要的Comment類、Reply類以前都已經自定義好了。因此如今就只須要在自定義模型類中繼承自 AbstractCommentModel<C>,並把泛型C替換成本身Comment類,最後實現抽象方法getComments()就能夠了。注意:getComments()方法必須實現並返回對應的List,被確保返回的這個List非null

2.3.1.五、總結

好了,到如今爲止,CommentView控件的數據模型就分析完了。好長篇幅。爲何呢?由於這個數據模型對於整個框架來講過重要了。必須理解好數據模型才能輕鬆使用這個控件。因此必須詳細分析整個數據模型。

2.3.二、ViewHolder類

ViewHolder類是一個抽象類,是對於自定義回覆佈局的時候使用的,自定義評論佈局不須要繼承自此類。ViewHolder位於com.jidcoo.android.widget.commentview.view包下。

自定義評論佈局ViewHolder類沒有任何限制和要求不須要任何繼承。因此這裏不展現例子代碼了,例子能夠查看defaults包下的DefaultCommentHolder類。

可是對於自定義回覆佈局,有四點必須不按要求就報錯):
一、必須使用ViewHolder機制
二、使用的ViewHolder必須繼承自com.jidcoo.android.widget.commentview.view包下ViewHolder抽象類。
三、自定義的回覆佈局中最外層佈局必須爲LinearLayout
四、而且這個最外層的LinearLayout的android:id屬性必須設置爲「reply_rootView」,即android:id="@+id/reply_rootView"

這四點必須怎麼來的呢,下面看ViewHolder類的代碼:

/**
 * 自定義回覆佈局必須繼承此抽象類並在構造方法中調用父類構造方法,不然會報錯。<p>
 * <br></br>
 * 在自定義回覆佈局中最外層必須使用LinearLayout並將佈局id設置爲「reply_rootView」,不然會報錯。<p>
 * @author Jidcoo
 */
public abstract class ViewHolder {
    /**
     * 自定義佈局這個view必須爲非空
     */
    public LinearLayout rootView;
    public ViewHolder(View view){
        try {
            rootView=view.findViewById(R.id.reply_rootView);
        }catch (Exception e){
            throw  new RuntimeException("If you use a custom layout, the outermost layout must be a LinearLayout, and you need to set its id attribute value to \"reply_rootView\"");
        }
    }
}

在ViewHolder這個抽象類中有一個LinearLayout變量rootView,而後在構造方法中會在傳進來的自定義佈局的view裏面經過findViewById()實例化rootView。因此當找不到這個id或者這個id對應的類型不是LinearLayout的時候會拋出RuntimeException異常

至於爲何要實例化rootView這個變量,能夠看com.jidcoo.android.widget.commentview.adapter包下的ViewAdapter類的具體代碼實現就知道了。這裏不具體展開。

自定義回覆佈局時使用的ViewHolder的例子:

/**
 * 建立類繼承自抽象類ViewHolder
 * 而後在帶參構造方法中顯式調用父級構造方法super(view)把view傳進去
 * 而後再實例化本身自定義的佈局控件
 */
public class ReplyItemViewHolder extends ViewHolder {
    public TextView replyTextView;
    //……添加本身須要的控件
    //……
    //……添加本身須要的控件
    
    public ReplyItemViewHolder(View view) {
        super(view);
        //實例化對應的控件
        replyTextView=view.findViewById(R.id.replyTextView);
        
    }
}

2.3.三、回調類(Callback)

2.3.3.一、CustomCommentItemCallback(自定義評論佈局回調)
/**
 * 自定義評論佈局的回調<p></p>
 * 至關於Adapter的getView()方法<p></p>
 * 與Adapter的getView()方法使用同樣<p></p>
 * 泛型接口中的泛型對應評論數據模型,即繼承在CommentEnable,查看{@link CommentEnable}
 * @author Jidcoo
 */
public interface CustomCommentItemCallback<C extends CommentEnable> {

    /**
     * 至關於adapter中的getView()方法
     * @param groupPosition Item所在groupPosition
     * @param comment 泛型評論數據
     * @param inflater  LayoutInflater實例(非空)
     * @param convertView View
     * @param parent
     * @return
     */
    View buildCommentItem(int groupPosition, C comment, LayoutInflater inflater, View convertView, ViewGroup parent);
}

CustomCommentItemCallback是一個自定義評論佈局的泛型回調,非必須回調,當須要自定義評論佈局的時候實現這個回調,回調中的buildCommentItem()方法至關於Adapter中的getView()方法,在buildCommentItem()中使用自定義的佈局便可。

CustomCommentItemCallback< C extends CommentEnable>中的泛型C是你使用的評論數據模型,即該回調接受的類必須繼承自CommentEnable。引入自定義佈局須要的LayoutInflater實例已經保證是非null的,直接使用便可,不須要外建立一個新的實例下降性能。注意:在實現該方法時儘可能使用ViewHolder機制,不然控件的性能可能會降低。

當須要自定義評論佈局的時候實現這個回調。

例子

/**
 * 自定義評論佈局
 * 實現CustomCommentItemCallback<C>回調
 * 把自定義評論模型Comment放入泛型參數中
 * 而後實現buildCommentItem()方法進行自定義佈局
 */
public class CustomCommentItem implements CustomCommentItemCallback<Comment> {
    @Override
    public View buildCommentItem(int groupPosition,Comment comment, LayoutInflater inflater, View convertView, ViewGroup parent) {
        //使用ViewHolder機制
        //自定義評論佈局的ViewHolder沒有任何要求,不須要任何繼承
        //爲了更好的性能,儘可能使用ViewHolder機制
        CommentViewHolder commentViewHolder=null;
        if(convertView==null){
            //引入自定義佈局
            convertView=inflater.inflate(R.layout.my_commentItem_layout);
            //實例化CommentViewHolder
            commentViewHolder=new CommentViewHolder(convertView);
            convertView.setTag(commentViewHolder);
        }else{
            commentViewHolder=(CommentViewHolder)convertView.getTag();
        }
        //對控件進行操做
        //setText()……
        //對控件進行操做
        return convertView;
    }
}
2.3.3.二、CustomReplyItemCallback(自定義回覆佈局回調)
/**
 * 自定義回覆佈局的回調<p></p>
 * 至關於Adapter的getView()方法<p></p>
 * 與Adapter的getView()方法使用同樣<p></p>
 * 泛型接口中的泛型對應回覆數據模型,即繼承在ReplyEnable,查看{@link ReplyEnable}<p></p>
 * 注意:
 * <br></br>
 * 佈局:<p>
 * 一、自定義佈局xml中最外層佈局必須是LinearLayout而且把最外層佈局id設置爲reply_rootView<p>
 * ViewHolder:<p>
 * 一、在buildReplyItem()中自定義佈局初始化時,必須使用ViewHolder機制<p>
 * 二、初始化過程當中,自定義Holder必須繼承自com.jidcoo.android.widget.commentview.view.ViewHolder,查看{@link ViewHolder}<p>
 * @author Jidcoo
 */
public interface CustomReplyItemCallback<R extends ReplyEnable> {

    /**
     * 至關於adapter中的getView()方法
     * @param groupPosition 所在父級位置
     * @param childPosition 所在位置
     * @param isLastReply 是不是最後一條評論
     * @param convertView View
     * @param reply 泛型回覆數據
     * @param inflater  LayoutInflater實例(非空)
     * @param parent
     * @return
     */
    View buildReplyItem(int groupPosition, int childPosition, boolean isLastReply, R reply, LayoutInflater inflater, View convertView, ViewGroup parent);
}

CustomReplyItemCallback是一個自定義回覆佈局的泛型回調,非必須回調,當須要自定義回覆佈局的時候實現這個回調,回調中的buildReplyItem()方法至關於Adapter中的getView()方法,在buildReplyItem()中使用自定義的佈局便可。

CustomReplyItemCallback< R extends ReplyEnable>中的泛型R是你使用的回覆數據模型,即該回調接受的類必須繼承自ReplyEnable。引入自定義佈局須要的LayoutInflater實例已經保證是非null的,直接使用便可,不須要外建立一個新的實例下降性能。注意:在實現buildReplyItem()方法時有四點必須(不按要求就報錯):

一、必須使用ViewHolder機制
二、使用的ViewHolder必須繼承自com.jidcoo.android.widget.commentview.view包下ViewHolder抽象類。
三、自定義的回覆佈局中最外層佈局必須爲LinearLayout
四、而且這個最外層的LinearLayout的android:id屬性必須設置爲「reply_rootView」,即android:id="@+id/reply_rootView"

這裏的ViewHolder上邊說過了,這裏再也不重複。在ViewAdapter中有一段代碼是用來對ViewHolder作校驗的:

if(convertView.getTag() == null) {
            throw new RuntimeException("You should call convertView.getTag() method to use Holder instance as the tag of convertView");
        }else{
            Object object = convertView.getTag();
            if(object instanceof ViewHolder) {
                //some core code
            }else{
                throw new RuntimeException("The ReplyHolder must extent from ViewHolder");
            }
        }

當使用自定義佈局時,它會根據你返回的的自定義convertView進行getTag(),若是返回null,就拋出異常,因此在使用自定義佈局時必須使用convertView.setTag()把Holder保存起來(ViewHolder機制)

當getTag()不爲空的時候,又會去判斷返回的對象是否是繼承自ViewHolder,若是不是,就拋出異常

當須要自定義回覆佈局的時候實現這個回調。

例子

/**
 * 自定義評論佈局
 * 實現CustomReplyItemCallback<R>回調
 * 把自定義回覆模型Reply放入泛型參數中
 * 而後實現buildReplyItem()方法進行自定義佈局
 */
public class CustomReplyItem implements CustomReplyItemCallback<Reply> {

    @Override
    public View buildReplyItem(int groupPosition, int childPosition, boolean isLastReply, Reply reply, LayoutInflater inflater, View convertView, ViewGroup parent) {
        //必須使用ViewHolder機制
        //自定義ReplyItemViewHolder必須繼承自com.jidcoo.android.widget.commentview.view.ViewHolder
        //在自定義回覆佈局中最外層必須使用LinearLayout
        //並將這個LinearLayout的id設置爲「reply_rootView」,不然會報錯。
        ReplyItemViewHolder replyItemViewHolder=null;
        if(convertView==null){
            //引入自定義佈局
            convertView=inflater.inflate(R.layout.my_replyItem_layout);
            //實例化ReplyItemViewHolder
            replyItemViewHolder=new ReplyItemViewHolder(convertView);
            //必須把ReplyItemViewHolder緩存在convertView中
            //不然報錯
            convertView.setTag(replyItemViewHolder);
            //必須把ReplyItemViewHolder緩存在convertView中
        }else{
            replyItemViewHolder=(ReplyItemViewHolder)convertView.getTag();
        }
        //對控件進行操做
        //setText()……
        //對控件進行操做
        return convertView;
    }
}
2.3.3.三、OnPullRefreshCallback(下拉刷新回調)
/**
 * CommentView下拉刷新回調
 * @author Jidcoo
 */
public interface OnPullRefreshCallback {
    /**
     * 刷新回調
     */
    void refreshing();
    /**
     * 刷新完成回調
     */
    void complete();

    /**
     * 刷新失敗
     * @param msg 錯誤信息
     */
    void failure(String msg);
}

OnPullRefreshCallback是下拉刷新回調,非必須回調。當實現了這個回調後能夠進行下拉刷新,不實現就沒法下拉刷新。看具體需求。

須要下拉刷新功能時實現這個回調。

例子:

public class MyOnPullRefreshCallback implements OnPullRefreshCallback {
    @Override
    public void refreshing() {
        //該方法被調用,表示控件正在刷新狀態
        //實現你的刷新邏輯
        //commentView.refreshComplete(AbstractCommentModel model): 刷新數據完成後調用
        //commentView.refreshFailed(String,boolean): 刷新出現錯誤時調用,能夠控制是否顯示錯誤視圖
    }

    @Override
    public void complete() {

    }

    @Override
    public void failure(String msg) {
        //當刷新失敗後顯示調用commentView.refreshFailed(String,boolean)方法後
        //這個方法會被調用
        //msg是refreshFailed()方法中傳進來的錯誤信息

    }
}
2.3.3.四、OnCommentLoadMoreCallback(上拉加載更多回調)
/**
 * CommentView加載更多評論回調
 * @author Jidcoo
 */
public interface OnCommentLoadMoreCallback{

    /**
     * 上拉加載更多評論回調
     * @param currentPage 當前頁碼
     * @param willLoadPage 下一個頁碼
     * @param isLoadedAllPages 是否已經加載完全部數據
     */
    void loading(int currentPage,int willLoadPage,boolean isLoadedAllPages);

    /**
     * 上拉加載更多評論完成
     */
    void complete();

    /**
     * 加載失敗
     * @param msg 錯誤信息
     */
    void failure(String msg);
}

OnCommentLoadMoreCallback是上拉加載更多評論回調,非必須回調。當實現了這個回調後能夠進行上拉加載更多評論,不實現就沒法上拉加載更多評論。看具體需求。

須要上拉加載更多評論功能時實現這個回調就能夠了,和OnPullRefreshCallback的例子基本一致。具體使用例子能夠查看相關源碼。

2.3.3.五、OnReplyLoadMoreCallback(加載更多回復回調)
/**
 * CommentView加載更多回復回調<p></p>
 * 泛型接口中的泛型R是回覆數據模型,必須繼承自ReplyEnable,查看{@link ReplyEnable}
 * @author Jidcoo
 */
public interface OnReplyLoadMoreCallback<R extends ReplyEnable> {

    /**
     * 加載更多回復回調
     * @param reply 對應回覆的數據
     * @param willLoadPage
     */
    void loading(R reply, int willLoadPage);

    /**
     * 加載更多回復完成回調
     */
    void complete();

    /**
     * 加載失敗
     * @param msg 錯誤信息
     */
    void failure(String msg);
}

OnReplyLoadMoreCallback是一個泛型回調,是加載更多回復回調,非必須回調。OnReplyLoadMoreCallback< R extends ReplyEnable>接受一個繼承自ReplyEnable類的自定義回覆數據模型類。具體使用例子能夠查看相關源碼。

2.3.3.六、OnScrollCallback(滾動事件回調)
/**
 * CommentView 滾動事件回調
 * @author Jidcoo
 */
public interface OnScrollCallback {
    /**
     * 滾動回調
     * @param view
     * @param firstVisibleItem
     * @param visibleItemCount
     * @param totalItemCount
     */
    void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int
            totalItemCount);

    /**
     * 滾動狀態回調
     * @param view
     * @param scrollState
     */
    void onScrollStateChanged(AbsListView view,int scrollState);

    /**
     * 滾動回調
     * @param v
     * @param scrollX
     * @param scrollY
     * @param oldScrollX
     * @param oldScrollY
     */
    void onScrollChange(View v, int scrollX, int scrollY, int oldScrollX, int oldScrollY);

}

OnScrollCallback提供了控件的全部滾動事件的回調,非必須回調。能夠經過實現這個回調來監聽控件滾動事件。

2.3.3.七、OnItemClickCallback(Item點擊事件回調)
/**
 * CommentView中評論Item和回覆Item點擊事件回調
 * 泛型接口中的C、R泛型:<p></p>
 * 一、C是評論數據模型,必須繼承自CommentEnable,查看{@link CommentEnable}<p></p>
 * 二、R是回覆數據模型,必須繼承自ReplyEnable,查看{@link ReplyEnable}<p></p>
 * @author Jidcoo
 */
public interface OnItemClickCallback<C extends CommentEnable,R extends ReplyEnable> {
    /**
     * 評論Item點擊回調事件
     * @param position 被點擊Item的位置
     * @param comment 被點擊Item所對應的數據
     * @param view 被點擊Item的View
     */
    void commentItemOnClick(int position, C comment, View view);

    /**
     * 回覆Item點擊回調事件
     * @param c_position 被點擊Item所在的父級位置
     * @param r_position 被點擊Item所在位置
     * @param reply 被點擊Item所對應的數據
     * @param view 被點擊Item的View
     */
    void replyItemOnClick(int c_position, int r_position, R reply, View view);
}

OnItemClickCallback類是一個接受泛型參數的Item點擊事件回調,OnItemClickCallback<C extends CommentEnable,R extends ReplyEnable>中泛型C、R對應的就是自定義評論數據模型和回覆數據模型。當控件中評論Item和回覆Item被點擊時,回調中相對應的commentItemOnClick和replyItemOnClick方法就會被調用。

須要監聽控件Item的點擊事件就實現這個接口。具體使用例子能夠查看相關源碼。

2.3.3.八、CallbackBuilder(回調集)
public final class CallbackBuilder {
        ///////***Callback***//////
        public OnPullRefreshCallback onPullRefreshCallback;//下拉刷新回調
        public OnCommentLoadMoreCallback onCommentLoadMoreCallback;//加載更多評論回調
        public OnReplyLoadMoreCallback onReplyLoadMoreCallback;//加載更多回復回調
        public OnItemClickCallback onItemClickCallback;//點擊事件回調
        public OnScrollCallback onScrollCallback;//滾動事件回調
        public CustomCommentItemCallback customCommentItemCallback;//自定義評論佈局回調
        public CustomReplyItemCallback customReplyItemCallback;//自定義回覆佈局回調
        ///////***Callback***//////
        public CallbackBuilder setOnPullRefreshCallback(OnPullRefreshCallback onPullRefreshCallback) {
            this.onPullRefreshCallback = onPullRefreshCallback;
            return this;
        }

        public CallbackBuilder setOnCommentLoadMoreCallback(OnCommentLoadMoreCallback onCommentLoadMoreCallback) {
            this.onCommentLoadMoreCallback = onCommentLoadMoreCallback;
            return this;
        }

        public CallbackBuilder setOnReplyLoadMoreCallback(OnReplyLoadMoreCallback onReplyLoadMoreCallback) {
            this.onReplyLoadMoreCallback = onReplyLoadMoreCallback;
            return this;
        }

        public CallbackBuilder setOnItemClickCallback(OnItemClickCallback onItemClickCallback) {
            this.onItemClickCallback = onItemClickCallback;
            return this;
        }

        public CallbackBuilder setOnScrollCallback(OnScrollCallback onScrollCallback) {
            this.onScrollCallback = onScrollCallback;
            return this;
        }

        public CallbackBuilder customCommentItem(CustomCommentItemCallback customCommentItemCallback) {
            this.customCommentItemCallback = customCommentItemCallback;
            return this;
        }

        public CallbackBuilder customReplyItem(CustomReplyItemCallback customReplyItemCallback) {
            this.customReplyItemCallback = customReplyItemCallback;
            return this;
        }
        public CommentView buildCallback() {
            return initialize(this);
        }
    }

CallbackBuilder是一個構建控件的全部回調的Builder類,是CommentView類中的內部類。上面說到的全部的Callback都須要經過這個Builder類進行設置,構建回調須要獲取這個Builder類的實例,而後再往這個Builder類設置須要的回調。

獲取這個Builder類的實例經過CommentView.callbackBuilder()方法獲取。當Builder實例爲NULL時返回一個新實例,不爲NULL就返回原來構建的實例。

能夠看到,在buildCallback()方法中會調用CommentView的initialize()方法對Builder類的回調實例進行初始化和進一步裝載。注意:不管是否須要設置回調,buildCallback()方法必須調用,若是不調用buildCallback()方法,控件處於未初始化狀態,不會展現任何數據和效果。。具體緣由可自行查看com.jidcoo.android.widget.commentview.operator包下的CommentViewOperator類源碼。

也就是說,就算在不須要設置任何回調的狀況下,buildCallback()都必須調用。調用以下:

commentView.callbackBuilder().buildCallback();

初始化回調例子:

CommentView commentView;
commentView.callbackBuilder()
           .setOnPullRefreshCallback(你的回調實例)
           .onItemClickCallback(你的回調實例)
           ......
           //設置完成後必須調用CallbackBuilder的buildCallback()方法,不然設置的回調無效,控件也沒法正常顯示。
           //不管是否設置回調,buildCallback()方法都必須調用。
           .buildCallback();
2.3.3.九、總結

好了,到如今爲止,CommentView控件的回調類基本介紹完成。寫的時候感受十分囉嗦,但最後仍是寫下來了。由於這些回調類雖然都是非必須回調,可是想實現更多功能都須要用到這些回調

2.四、樣式配置器

/**
 * 非可自定義控件樣式配置器
 * <p>該樣式配置器針對非自定義控件的樣式的具體配置</p>
 * <p>可根據具體需求配置這部分的控件的樣式</p>
 * <p>自定義樣式配置器必須繼承自本類</p>
 * <br></br>
 * <h3>樣式變量說明:</h3>
 * <p>一、refreshViewColor </p>
 * <p>類型:String</p>
 * <p>說明:下拉刷新後出現的圓形滾動條的顏色</p>
 * <p>賦值樣例:#000000</p>
 * <p>二、lmv_showText(正常顯示狀態) </p>
 * <p>類型:String</p>
 * <p>說明:當回覆數據Item能夠加載更多(分頁加載)時,在最後一條回覆Item的View下面顯示的文字</p>
 * <p>賦值樣例:展開更多回復</p>
 * <p>三、lmv_textSize </p>
 * <p>類型:int</p>
 * <p>說明:第二條說明中的文字大小</p>
 * <p>賦值樣例:14</p>
 * <p>四、lmv_textColor </p>
 * <p>類型:String</p>
 * <p>說明:第二條說明中的文字顏色</p>
 * <p>賦值樣例:#000000</p>
 * <p>五、lmv_textStyle </p>
 * <p>類型:Typeface,查看{@link Typeface}</p>
 * <p>說明:第二條說明中的文字樣式(正常、斜體、加粗)</p>
 * <p>賦值樣例:Typeface.defaultFromStyle(Typeface.NORMAL)</p>
 * <p>六、lmv_loading_showText(加載中狀態) </p>
 * <p>類型:String</p>
 * <p>說明:當回覆數據Item能夠加載更多(分頁加載)時,在最後一條回覆Item的加載更多View被點擊後顯示的文字</p>
 * <p>賦值樣例:加載中</p>
 * <p>七、lmv_loading_textSize </p>
 * <p>類型:int</p>
 * <p>說明:第六條說明中的文字大小</p>
 * <p>賦值樣例:14</p>
 * <p>八、lmv_loading_textColor </p>
 * <p>類型:String</p>
 * <p>說明:第六條說明中的文字顏色</p>
 * <p>賦值樣例:#000000</p>
 * <p>九、lmv_loading_textStyle </p>
 * <p>類型:Typeface,查看{@link Typeface}</p>
 * <p>說明:第六條說明中的文字樣式(正常、斜體、加粗)</p>
 * <p>賦值樣例:Typeface.defaultFromStyle(Typeface.NORMAL)</p>
 * <p>十、lmv_loading_progressBarColor </p>
 * <p>類型:String</p>
 * <p>說明:當最後一條回覆ItemView的可加載更多View狀態爲正在加載狀態時,
 * 第六條說明中的文字右邊會顯示一個圓形progressBar以加強UI加載效果,
 * 該屬性是這個progressBar的顏色值</p>
 * <p>賦值樣例:#000000</p>
 * <p>十一、lmv_loading_progressBarSize </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第十條說明中的progressBar控件的大小尺寸</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>十二、lmv_adjustMargins </p>
 * <p>類型:Boolean(默認爲false)</p>
 * <p>說明:是否調整最後一條回覆Item的加載更多View的邊距,若是自定義回覆佈局,可能須要
 * 調整這個View的邊距來使佈局更美觀,通常狀況下都須要調整邊距,因此這個屬性通常都爲true</p>
 * <p>賦值樣例:true</p>
 * <p>1三、lmv_adjustMarginsLeft </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第十二條說明中的view的左邊距,若是lmv_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>1四、lmv_adjustMarginsTop </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第十二條說明中的view的上邊距,若是lmv_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>1五、lmv_adjustMarginsRight </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第十二條說明中的view的右邊距,若是lmv_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>1六、lmv_adjustMarginsBottom </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第十二條說明中的view的下邊距,若是lmv_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>1七、dividerColor </p>
 * <p>類型:String</p>
 * <p>說明:控件Item的分割線顏色(包含一級分割線和二級分割線)</p>
 * <p>賦值樣例:#000000</p>
 * <p>1八、dividerHeight </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:控件Item的分割線高度(包含一級分割線和二級分割線)</p>
 * <p>賦值樣例:ViewUtil.dpToPx(1),高度通常爲1dp</p>
 * <p>1九、isDrawChildDivider </p>
 * <p>類型:Boolean(默認爲false)</p>
 * <p>說明:是否繪製某一條評論下的最後一條回覆Item與下一條評論的Item的<u>分割線</u>,默認不繪製,
 * 可根據具體須要選擇是否繪製</p>
 * <p>賦值樣例:true</p>
 * <p>20、c_divider_adjustMargins </p>
 * <p>類型:Boolean(默認爲false)</p>
 * <p>說明:是否調整第十七條說明中的分割線的邊距,若是isDrawDivider爲false,則此屬性無效。
 * 由於第十九條說明中的分割線寬度固定爲match_parent(即鋪滿全屏),可是能夠經過調整分割線的邊距
 * 來間接調整分割線的寬度和位置,可根據具體須要選擇是否調整分割線的邊距</p>
 * <p>賦值樣例:true</p>
 * <p>2一、c_divider_adjustMarginsLeft </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第十九條說明中的分割線的左邊距,若是divider_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(1)</p>
 * <p>2二、c_divider_adjustMarginsTop </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第十九條說明中的分割線的上邊距,若是divider_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(1)</p>
 * <p>2三、c_divider_adjustMarginsRight </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第十九條說明中的分割線的右邊距,若是divider_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(1)</p>
 * <p>2四、c_divider_adjustMarginsBottom </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第十九條說明中的分割線的下邊距,若是divider_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(1)</p>
 * <p>2五、f_divider_adjustMargins </p>
 * <p>類型:Boolean(默認爲false)</p>
 * <p>說明:是否調整評論item的分割線的邊距</p>
 * <p>賦值樣例:true</p>
 * <p>2六、f_divider_adjustMarginsLeft </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:評論item的分割線的左邊距</p>
 * <p>賦值樣例:ViewUtil.dpToPx(1)</p>
 * <p>2七、f_divider_adjustMarginsTop </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:評論item的分割線的上邊距</p>
 * <p>賦值樣例:ViewUtil.dpToPx(1)</p>
 * <p>2八、f_divider_adjustMarginsRight </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:評論item的分割線的右邊距</p>
 * <p>賦值樣例:ViewUtil.dpToPx(1)</p>
 * <p>2九、f_divider_adjustMarginsBottom </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:評論item的分割線的下邊距</p>
 * <p>賦值樣例:ViewUtil.dpToPx(1)</p>
 * <p>30、lm_footerProgressBarSize </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:上拉刷新時,控件底部會有一個圓形ProgressBar來增長加載中的UI效果,此屬性爲ProgressBar的大小</p>
 * <p>賦值樣例:ViewUtil.dpToPx(1)</p>
 * <p>3一、lm_footerProgressBarColor </p>
 * <p>類型:String</p>
 * <p>說明:第三十條說明中的ProgressBar的顏色</p>
 * <p>賦值樣例:#000000</p>
 * <p>3二、lm_footerProgressBar_adjustMargins </p>
 * <p>類型:Boolean(默認爲false)</p>
 * <p>說明:是否調整第三十條說明中的ProgressBar的邊距</p>
 * <p>賦值樣例:true</p>
 * <p>3三、lm_footerProgressBar_adjustMarginsLeft </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第三十條說明中的ProgressBar的左邊距,若是lm_footerProgressBar_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>3四、lm_footerProgressBar_adjustMarginsTop </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第三十條說明中的ProgressBar的上邊距,若是lm_footerProgressBar_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>3五、lm_footerProgressBar_adjustMarginsRight </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第三十條說明中的ProgressBar的右邊距,若是lm_footerProgressBar_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>3六、lm_footerProgressBar_adjustMarginsBottom </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第三十條說明中的ProgressBar的下邊距,若是lm_footerProgressBar_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>3七、lm_footer_text</p>
 * <p>類型:String</p>
 * <p>說明:當全部數據加載完成後控件底部textView顯示的文字</p>
 * <p>賦值樣例:哈哈,全部評論都在這了</p>
 * <p>3八、lm_footer_textColor</p>
 * <p>類型:String</p>
 * <p>說明:第三十七條說明中的textView文字的顏色</p>
 * <p>賦值樣例:#666666</p>
 * <p>3九、lm_footer_textSize</p>
 * <p>類型:int</p>
 * <p>說明:第三十七條說明中的textView文字的大小</p>
 * <p>賦值樣例:14</p>
 * <p>40、lm_footer_textStyle</p>
 * <p>類型:Typeface</p>
 * <p>說明:第三十七條說明中的textView文字的樣式(正常,斜體、加粗)</p>
 * <p>賦值樣例:Typeface.defaultFromStyle(Typeface.NORMAL)</p>
 * <p>4一、lm_footer_text_adjustMargins </p>
 * <p>類型:Boolean(默認爲false)</p>
 * <p>說明:是否調整第三十七條說明中的textView的邊距</p>
 * <p>賦值樣例:true</p>
 * <p>4二、lm_footer_text_adjustMarginsLeft </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第三十七條說明中的textView的左邊距,若是lm_footer_text_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>4三、lm_footer_text_adjustMarginsTop </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第三十七條說明中的textView的上邊距,若是lm_footer_text_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>4四、lm_footer_text_adjustMarginsRight </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第三十七條說明中的textView的右邊距,若是lm_footer_text_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <p>4五、lm_footer_text_adjustMarginsBottom </p>
 * <p>類型:int(要求單位:px,由於須要把(dp/dip值轉換爲px值))</p>
 * <p>說明:第三十七條說明中的textView的下邊距,若是lm_footer_text_adjustMargins爲false,則此屬性無效</p>
 * <p>賦值樣例:ViewUtil.dpToPx(14)</p>
 * <br><br>
 * @author Jidcoo
 */
public abstract class ViewStyleConfigurator{
    /**
     * 下拉刷新控件顏色
     */
    public  String refreshViewColor;

    //*********//回覆加載更多控件樣式//*************//
    //Part1//
    public  String lmv_showText;
    public  int lmv_textSize;
    public  String lmv_textColor;
    public  Typeface lmv_textStyle;
    //Part2//
    public  String lmv_loading_showText;
    public  int lmv_loading_textSize;
    public  String lmv_loading_textColor;
    public  Typeface lmv_loading_textStyle;
    public  String lmv_loading_progressBarColor;
    public  int lmv_loading_progressBarSize;
    //Part3//
    public  boolean lmv_adjustMargins;
    public  int lmv_adjustMarginsLeft;//dp
    public  int lmv_adjustMarginsTop;//dp
    public  int lmv_adjustMarginsRight;//dp
    public  int lmv_adjustMarginsBottom;//dp
    //*********//回覆加載更多控件樣式//*************//

    //*********//分割線總樣式//*************//
    public  String dividerColor;
    public  int dividerHeight;//dp
    //*********//分割線總樣式//*************//

    //*********//回覆Item最後一項的下劃線(分樣式)//*************//
    public boolean isDrawChildDivider;
    public  boolean c_divider_adjustMargins;
    public  int c_divider_adjustMarginsLeft;//dp
    public  int c_divider_adjustMarginsTop;//dp
    public  int c_divider_adjustMarginsRight;//dp
    public  int c_divider_adjustMarginsBottom;//dp
    //*********//回覆Item最後一項的下劃線(分樣式)//*************//


    //*********//評論Item的分割線(分樣式)//*************//
    /**
     * 評論Item的分割線顏色、高度與回覆Item的分割線同樣
     */
    public boolean  f_divider_adjustMargins;
    public  int f_divider_adjustMarginsLeft;//dp
    public  int f_divider_adjustMarginsTop;//dp
    public  int f_divider_adjustMarginsRight;//dp
    public  int f_divider_adjustMarginsBottom;//dp
    //*********//評論Item的分割線(分樣式)//*************//

    //*********//底部上拉加載更多圓形ProgressBar的大小和顏色//*************//
    public int lm_footerProgressBarSize=12;//dp
    public String lm_footerProgressBarColor;
    public boolean  lm_footerProgressBar_adjustMargins;
    public  int lm_footerProgressBar_adjustMarginsLeft;//dp
    public  int lm_footerProgressBar_adjustMarginsTop;//dp
    public  int lm_footerProgressBar_adjustMarginsRight;//dp
    public  int lm_footerProgressBar_adjustMarginsBottom;//dp
    //*********//底部上拉加載更多圓形ProgressBar的大小和顏色//*************//


    //*********//底部上拉加載更多完成後的textView//*************//
    //文本,顏色、大小、樣式、邊距
    public String lm_footer_text;
    public String lm_footer_textColor;
    public int lm_footer_textSize=12;
    public Typeface lm_footer_textStyle;
    public boolean  lm_footer_text_adjustMargins;
    public  int lm_footer_text_adjustMarginsLeft;//dp
    public  int lm_footer_text_adjustMarginsTop;//dp
    public  int lm_footer_text_adjustMarginsRight;//dp
    public  int lm_footer_text_adjustMarginsBottom;//dp
    //*********//底部上拉加載更多完成後的textView//*************//
    
}

ViewStyleConfigurator是樣式配置器抽象類。該樣式配置器針對固有控件的樣式的具體配置。抽象類中的全部變量都是用來設置固有控件的樣式。全部的樣式變量的解釋和說明都有詳細充分的註釋。在這就不一一說明了。

在自定義樣式的時候建立一個類繼承自這個抽象類,而後對須要自定義的樣式變量賦值就能夠了。

例子:(這裏貼出的是項目中的默認樣式配置器,自定義樣式配置器能夠參照defaults包下的DefaultViewStyleConfigurator類)

/**
 * 默認樣式配置器
 * <p>此配置器爲標準模板</p>
 * <p>自定義樣式配置器必須繼承自ViewStyleConfigurator,查看{@link ViewStyleConfigurator}</p>
 * <p>樣式變量說明,請查看{@link ViewStyleConfigurator}</p>
 * <p>實際上,自定義樣式配置器能夠直接複製本類,而後再根據具體把樣式修改就行了</p>
 * @author Jidcoo
 */
public class DefaultViewStyleConfigurator extends ViewStyleConfigurator {
   
    public DefaultViewStyleConfigurator(Context context) {
        /**
         * 下拉刷新控件顏色
         */
        refreshViewColor = "#D81B60";
        //*********//回覆加載更多控件樣式//*************//
        //Part1//
        lmv_showText = "展開更多回復";
        lmv_textSize = 14;
        lmv_textColor = "#D81B60";
        lmv_textStyle = Typeface.defaultFromStyle(Typeface.NORMAL);
        //Part2//
        lmv_loading_showText = "加載中";
        lmv_loading_textSize = 14;
        lmv_loading_textColor = "#666666";
        lmv_loading_textStyle = Typeface.defaultFromStyle(Typeface.NORMAL);
        lmv_loading_progressBarColor = "#666666";
        lmv_loading_progressBarSize = ViewUtil.dpToPx(14, context);
        //Part3//
        lmv_adjustMargins = true;
        lmv_adjustMarginsLeft = ViewUtil.dpToPx(88, context);//dp
        lmv_adjustMarginsTop = ViewUtil.dpToPx(5, context);//dp
        lmv_adjustMarginsRight = 0;//dp
        lmv_adjustMarginsBottom = ViewUtil.dpToPx(5, context);//dp
        //*********//回覆加載更多控件樣式//*************//
        //*********//分割線總樣式//*************//
        dividerColor = "#f0f0f0";
        dividerHeight = ViewUtil.dpToPx(1, context);//dp
        //*********//分割線總樣式//*************//
        //*********//回覆Item最後一項的下劃線//*************//
        isDrawChildDivider = true;
        c_divider_adjustMargins = true;
        c_divider_adjustMarginsLeft = ViewUtil.dpToPx(88, context);//dp
        c_divider_adjustMarginsTop = ViewUtil.dpToPx(5, context);//dp
        c_divider_adjustMarginsRight = 0;//dp
        c_divider_adjustMarginsBottom = ViewUtil.dpToPx(5, context);//dp
        //*********//回覆Item最後一項的下劃線//*************//
        //*********//評論Item的分割線//*************//
        f_divider_adjustMargins = false;
        f_divider_adjustMarginsLeft = 0;//dp
        f_divider_adjustMarginsTop = 0;//dp
        f_divider_adjustMarginsRight = 0;//dp
        f_divider_adjustMarginsBottom = 0;//dp
        //*********//評論Item的分割線//*************//
        //*********//底部上拉加載更多圓形ProgressBar的大小和顏色//*************//
        lm_footerProgressBarSize = ViewUtil.dpToPx(20, context);//dp
        lm_footerProgressBarColor = "#c3c8cb";
        lm_footerProgressBar_adjustMargins = false;
        lm_footerProgressBar_adjustMarginsLeft = 0;//dp
        lm_footerProgressBar_adjustMarginsTop = 0;//dp
        lm_footerProgressBar_adjustMarginsRight = 0;//dp
        lm_footerProgressBar_adjustMarginsBottom = 0;//dp
        //*********//底部上拉加載更多圓形ProgressBar的大小和顏色//*************//


        //*********//底部上拉加載更多完成後的textView//*************//
        //文本,顏色、大小、樣式、邊距
        lm_footer_text = "評論都給你看完了哦~";
        lm_footer_textColor = "#c3c8cb";
        lm_footer_textSize = 14;
        lm_footer_textStyle = Typeface.defaultFromStyle(Typeface.NORMAL);
        lm_footer_text_adjustMargins = false;
        lm_footer_text_adjustMarginsLeft = 0;//dp
        lm_footer_text_adjustMarginsTop = 0;//dp
        lm_footer_text_adjustMarginsRight = 0;//dp
        lm_footer_text_adjustMarginsBottom = 0;//dp
        //*********//底部上拉加載更多完成後的textView//*************//
    }
}

2.五、總結

超長的篇幅解釋說明使用控件中須要用到的框架類,能夠說是使用CommentView的理論部分。超長超囉嗦,可是這些說明都是很是重要的,也是必須的。爲何這麼說呢?由於上面的這些框架類的說明和例子都是在使用CommentView過程當中業務層須要用到的,也就是說理解了上面長篇大論的解釋後,使用CommentView就很是簡單了

因此儘管十分囉嗦,可是有些東西須要特別說明一下。以上都是理論部分(十分枯燥、又長又臭……)有了上述這些對整個控件框架的總體瞭解,使用這個控件庫就很是容易了

注意:項目是使用AndroidX庫開發,部分控件是AndroidX庫的控件,若是你的項目沒有遷移到AndroidX庫開發,能夠把CommentView控件庫中的那些AndroidX庫控件修改成你當前項目的Android庫的控件就能夠了。

下面就要講CommentView實際使用部分了(理解了上面的理論,說實話使用沒有任何技術含量)。

3、控件的使用

3.一、控件方法介紹

如下是控件對外公佈的全部業務方法。
方法 參數 說明
.callbackBuilder() \ 獲取CallbackBuilder實例設置須要的回調, 不管是否設置回調,都必須調用.callbackBuilder().buildCallback()方法完成初始化。而且初始化必須在loadComplete()調用以前完成。
.loadComplete() AbstractCommentModel 首次加載數據時調用此方法, 注意:設置回調,設置空數據視圖、設置錯誤視圖、設置自定義樣式配置器,設置頭佈局,這5個方法都必需要在loadComplete()這個方法調用以前調用,不然這5個方法失效。
.loadFailed() boolean isShowErrorView 首次加載數據失敗時調用,boolean參數表示是否顯示錯誤視圖。
.refreshComplete() AbstractCommentModel 刷新數據完成後調用,傳入刷新後的數據實體類。該方法調用後OnPullRefreshCallback的complete()方法會被調用。
.refreshFailed() String msg,boolean isShowErrorView 數據刷新失敗時調用,msg表示錯誤信息,boolean參數表示是否顯示錯誤視圖。該方法調用後OnPullRefreshCallback的failure(String msg)方法會被調用。
.loadMoreComplete() AbstractCommentModel 加載更多評論數據完成後調用,傳入一個數據實體類。該方法調用後OnCommentLoadMoreCallback的complete()方法會被調用。
.loadMoreFailed() String msg,boolean isShowErrorView 加載更多評論數據失敗時調用,msg表示錯誤信息,boolean參數表示是否顯示錯誤視圖。該方法調用後OnCommentLoadMoreCallback的failure(String msg)方法會被調用。
.loadMoreReplyComplete() AbstractCommentModel 加載更多回複數據完成後調用,傳入一個數據實體類。該方法調用後OnReplyLoadMoreCallback的complete()方法會被調用。
.loadMoreReplyFailed() String msg,boolean isShowErrorView 加載更多評論數據失敗時調用,msg表示錯誤信息,boolean參數表示是否顯示錯誤視圖。該方法調用後OnReplyLoadMoreCallback的failure(String msg)方法會被調用。
.getCommentList() \ 獲取全部評論數據返回List
.getReplyList() int position 根據position所在的評論獲取所在評論的全部回覆數據返回List
.addComment() C<?extend CommentEnable> comment 添加一條評論數據到當前的評論數據集合中
.addReply() R<?extend ReplyEnable> comment,int position 添加一條回覆數據到position所在的評論的回覆數據集合中
.setEmptyView() View view 爲控件設置空數據視圖,注意:此方法必須在loadComplete()方法調用前調用,也就是說要在首次加載數據前調用,不然此方法無效。
.setErrorView() View view 爲控件設置錯誤視圖,注意:此方法必須在loadComplete()方法調用前調用,也就是說要在首次加載數據前調用,不然此方法無效。
.addHeaderView() View view,boolean canClickable 爲控件添加頭視圖,而且設置該視圖是否響應點擊事件。注意:此方法建議在loadComplete()方法調用前調用。
.removeHeaderView() View view 移除當前控件的對應的頭視圖
.setViewStyleConfigurator() ViewStyleConfigurator 設置自定義的樣式配置器,注意:此方法必須在loadComplete()方法調用前調用,也就是說要在首次加載數據前調用,不然此方法無效。

3.二、基本使用

第一步:引入控件庫:

有兩種方法:

一、遠程倉庫

在module的build.gradle中添加jcenter倉庫:

buildscript {
    repositories {
        jcenter()
    }
}

而後在dependencies模塊中添加依賴便可:

implementation 'com.jidcoo.android.widget.commentview:CommentView:1.0.0'

二、本地倉庫

把源碼包下載下來,把commentview庫放在與當前module的同級。

而後在dependencies模塊中添加本地依賴便可:

implementation project(path: ':commentview')

第二步:引入控件:

控件的引入方法有兩種:

一、XML佈局文件中引入

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:id="@+id/container"
    android:layout_height="match_parent">


  <com.jidcoo.android.widget.commentview.CommentView
      android:id="@+id/commentView"
      android:layout_width="match_parent"
      android:layout_height="match_parent"/>
</LinearLayout>

而後在Activity中實例化控件。

二、Java代碼中動態建立

//控件容器
LinearLayout mContainer;
CommentView commentView=new CommentView(this,mContainer);

CommentView的構造方法:
public CommentView(Context context, ViewGroup attachTo)

第一個參數是Activity的上下文。第二個參數是ViewGroup,也就是將控件掛靠在這個指定的佈局上。注意:當attachTo參數爲空時,須要手動把控件添加到佈局中,不然控件將不會顯示

第三步:初始化控件:

設置自定義樣式配置器

若是使用默認樣式的話就不須要調用這個方法,若是使用自定義樣式配置器時調用該方法必須在loadComplete()調用前調用,不然該方法無效。

commentView.setViewStyleConfigurator(你的樣式配置器);

設置空數據視圖
若是不須要設置空數據視圖就不須要調用這個方法,若是須要設置空數據視圖時調用該方法必須在loadComplete()調用前調用,不然該方法無效。

commentView.setEmptyView(你的空數據視圖);

設置錯誤視圖
若是不須要設置錯誤視圖就不須要調用這個方法,若是須要設置錯誤視圖時調用該方法必須在loadComplete()調用前調用,不然該方法無效。

commentView.setErrorView(你的錯誤視圖);

添加控件頭視圖
若是不須要添加控件頭視圖就不須要調用這個方法,若是須要添加控件頭視圖時調用該方法建議在loadComplete()調用前調用。

commentView.addHeaderView(你的錯誤視圖,是否響應點擊事件);

第四步:初始化回調(很是重要,必須初始化):

不管是否須要設置回調,都要調用.buildCallback()方法完成初始化。

而且初始化回調的工做必需要在loadComplete()方法調用以前(即首次加載數據以前)完成,不然控件將沒法正常使用。

支持的回調:
一、CustomCommentItemCallback:自定義評論佈局回調
二、CustomReplyItemCallback:自定義回覆佈局回調
三、OnPullRefreshCallback:上拉刷新回調
四、OnCommentLoadMoreCallback:下拉加載更多評論回調
五、OnReplyLoadMoreCallback:加載更多回復回調
六、OnItemClickCallback:Item的點擊事件回調
七、OnScrollCallback:控件滾動事件回調

當須要設置回調時:

設置完回調後必須調用.buildCallback()方法,不然回調不會生效,控件也沒法正常使用。

commentView.callbackBuilder()
           .setOnPullRefreshCallback(你的回調實例)
           .onItemClickCallback(你的回調實例)
           ......設置更多回調
           ......設置更多回調
           //設置完成後必須調用CallbackBuilder的buildCallback()方法,不然設置的回調無效,控件也沒法正常顯示。
           //不管是否設置回調,buildCallback()方法都必須調用。
           .buildCallback();

當不須要設置回調時:

必須調用.buildCallback()方法,不然控件也沒法正常使用。

commentView.callbackBuilder().buildCallback();

第五步:設置數據:

當全部的初始化工做都完成後,就能夠請求後臺返回評論數據或加載本地數據爲控件設置數據了。完成設置數據後,控件就能正確顯示評論數據了。

commentView.loadComplete(你的數據模型實體類);

3.三、具體實例(自定義仿微信公衆號文章評論)

佈局文件:custom_use.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" 
    android:layout_width="match_parent"
    android:background="#f0f0f0"
    android:layout_height="match_parent">


  <com.jidcoo.android.widget.commentview.CommentView
      android:id="@+id/commentView"
      android:layout_width="match_parent"
      android:layout_height="match_parent"/>
</LinearLayout>

自定義評論佈局:custom_item_comment.xml

<?xml version="1.0" encoding="utf-8"?>
<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="wrap_content"
    android:orientation="horizontal">

    <com.jidcoo.android.widgettest.custom.SampleCircleImageView
        android:id="@+id/ico"
        android:layout_width="28dp"
        android:layout_height="28dp"
        android:layout_marginLeft="14dp"
        android:layout_marginTop="18dp"
        app:radius="2dp"
        app:src="@drawable/teaser" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:layout_marginRight="14dp"
        android:layout_marginBottom="4dp">


        <TextView
            android:id="@+id/user"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:textColor="#666666"
            android:textSize="13sp" />

        <ImageView
            android:id="@+id/comment_item_like"
            android:layout_width="18dp"
            android:layout_height="18dp"
            android:layout_alignParentRight="true"
            android:layout_marginRight="30dp"
            android:padding="3dp"
            android:src="@drawable/pxjh"
            android:visibility="visible" />

        <TextView
            android:id="@+id/prizes"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:textColor="#666666"
            android:textSize="13sp" />

        <TextView
            android:id="@+id/data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="8dp"
            android:layout_marginTop="18dp"
            android:textColor="#1C1C1C" />
    </RelativeLayout>


</LinearLayout>

自定義回覆佈局:custom_item_reply.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:id="@+id/reply_rootView"
    android:layout_height="wrap_content"
    android:orientation="vertical">

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal">

    <View
        android:id="@+id/line"
        android:layout_width="2dp"
        android:layout_height="wrap_content"
        android:layout_alignTop="@id/right"
        android:layout_alignBottom="@id/right"
        android:layout_marginLeft="52dp"
        android:layout_marginTop="2dp"
        android:layout_marginBottom="2dp"
        android:background="#c3c8cb" />


    <RelativeLayout
        android:id="@+id/right"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:layout_marginRight="14dp"
        android:layout_marginBottom="4dp"
        android:layout_toRightOf="@id/line">


        <TextView
            android:id="@+id/user"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="6dp"
            android:textColor="#666666"
            android:textSize="13sp" />

        <ImageView
            android:id="@+id/comment_item_like"
            android:layout_width="18dp"
            android:layout_height="18dp"
            android:layout_alignParentRight="true"
            android:layout_marginRight="30dp"
            android:padding="3dp"
            android:src="@drawable/pxjh"
            android:visibility="visible" />

        <TextView
            android:id="@+id/prizes"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_alignParentRight="true"
            android:textColor="#666666"
            android:textSize="13sp" />

        <TextView
            android:id="@+id/data"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="6dp"
            android:layout_marginTop="18dp"
            android:textColor="#1C1C1C" />
    </RelativeLayout>


</RelativeLayout>
</LinearLayout>

自定義數據模型:CustomCommentModel.java

import com.jidcoo.android.widget.commentview.model.AbstractCommentModel;
import com.jidcoo.android.widget.commentview.model.CommentEnable;
import com.jidcoo.android.widget.commentview.model.ReplyEnable;

import java.util.List;

public class CustomCommentModel extends AbstractCommentModel<CustomCommentModel.CustomComment> {
    public List<CustomComment> comments;

    @Override
    public List<CustomComment> getComments() {
        return comments;
    }

    public class CustomComment extends CommentEnable{
        public List<CustomReply> replies;
        public String posterName;
        public String data;

        public CustomComment() {
        
        }
        
        public void setReplies(List<CustomReply> replies) {
            this.replies = replies;
        }

        public String getPosterName() {
            return posterName;
        }

        public void setPosterName(String posterName) {
            this.posterName = posterName;
        }

        public String getData() {
            return data;
        }

        public void setData(String data) {
            this.data = data;
        }
        
        @Override
        public  List<CustomReply> getReplies() {
            return replies;
        }

        public class CustomReply extends ReplyEnable{
            public String replierName;
            public String data;

            public CustomReply() {

            }
            public String getReplierName() {
                return replierName;
            }

            public void setReplierName(String replierName) {
                this.replierName = replierName;
            }

            public String getData() {
                return data;
            }

            public void setData(String data) {
                this.data = data;
            }
        }
    }
}

評論佈局ViewHolder:CustomCommentViewHolder.java

import android.view.View;
import android.widget.TextView;
import com.jidcoo.android.widgettest.R;

public class CustomCommentViewHolder {
      public TextView userName,prizes,comment;
      public SampleCircleImageView ico;

    public CustomCommentViewHolder(View view) {
        userName=view.findViewById(R.id.user);
        prizes=view.findViewById(R.id.prizes);
        comment=view.findViewById(R.id.data);
       ico=view.findViewById(R.id.ico);
    }
}

回覆佈局ViewHolder:CustomReplyViewHolder.java

import android.view.View;
import android.widget.TextView;
import com.jidcoo.android.widget.commentview.view.ViewHolder;
import com.jidcoo.android.widgettest.R;

public class CustomReplyViewHolder extends ViewHolder {
    public TextView userName,prizes,reply;

    public CustomReplyViewHolder(View view) {
        super(view);
        userName=view.findViewById(R.id.user);
        prizes=view.findViewById(R.id.prizes);
        reply=view.findViewById(R.id.data);
    }
}

自定義樣式配置器:CustomViewStyleConfigurator.java

import android.content.Context;
import android.graphics.Typeface;
import com.jidcoo.android.widget.commentview.utils.ViewUtil;
import com.jidcoo.android.widget.commentview.view.ViewStyleConfigurator;


public class CustomViewStyleConfigurator extends ViewStyleConfigurator {

    public CustomViewStyleConfigurator(Context context) {
        /**
         * 下拉刷新控件顏色
         */
        refreshViewColor = "#D81B60";
        //*********//回覆加載更多控件樣式//*************//
        //Part1//
        lmv_showText = "展開更多回復";
        lmv_textSize = 14;
        lmv_textColor = "#D81B60";
        lmv_textStyle = Typeface.defaultFromStyle(Typeface.NORMAL);
        //Part2//
        lmv_loading_showText = "加載中";
        lmv_loading_textSize = 14;
        lmv_loading_textColor = "#666666";
        lmv_loading_textStyle = Typeface.defaultFromStyle(Typeface.NORMAL);
        lmv_loading_progressBarColor = "#666666";
        lmv_loading_progressBarSize = ViewUtil.dpToPx(14, context);
        //Part3//
        lmv_adjustMargins = true;
        lmv_adjustMarginsLeft = ViewUtil.dpToPx(52, context);//dp
        lmv_adjustMarginsTop = ViewUtil.dpToPx(5, context);//dp
        lmv_adjustMarginsRight = 0;//dp
        lmv_adjustMarginsBottom = ViewUtil.dpToPx(5, context);//dp
        //*********//回覆加載更多控件樣式//*************//
        //*********//分割線總樣式//*************//
        dividerColor = "#00000000";
        dividerHeight = ViewUtil.dpToPx(1, context);//dp
        //*********//分割線總樣式//*************//
        //*********//回覆Item最後一項的下劃線//*************//
//        isDrawChildDivider = false;
//        c_divider_adjustMargins = true;
//        c_divider_adjustMarginsLeft = ViewUtil.dpToPx(88, context);//dp
//        c_divider_adjustMarginsTop = ViewUtil.dpToPx(5, context);//dp
//        c_divider_adjustMarginsRight = 0;//dp
//        c_divider_adjustMarginsBottom = ViewUtil.dpToPx(5, context);//dp
//        //*********//回覆Item最後一項的下劃線//*************//
//        //*********//評論Item的分割線//*************//
//        f_divider_adjustMargins = false;
//        f_divider_adjustMarginsLeft = 0;//dp
//        f_divider_adjustMarginsTop = 0;//dp
//        f_divider_adjustMarginsRight = 0;//dp
//        f_divider_adjustMarginsBottom = 0;//dp
        //*********//評論Item的分割線//*************//
        //*********//底部上拉加載更多圓形ProgressBar的大小和顏色//*************//
        lm_footerProgressBarSize = ViewUtil.dpToPx(20, context);//dp
        lm_footerProgressBarColor = "#666666";
        lm_footerProgressBar_adjustMargins = false;
        lm_footerProgressBar_adjustMarginsLeft = 0;//dp
        lm_footerProgressBar_adjustMarginsTop = 0;//dp
        lm_footerProgressBar_adjustMarginsRight = 0;//dp
        lm_footerProgressBar_adjustMarginsBottom = 0;//dp
        //*********//底部上拉加載更多圓形ProgressBar的大小和顏色//*************//


        //*********//底部上拉加載更多完成後的textView//*************//
        //文本,顏色、大小、樣式、邊距
        lm_footer_text = "到底了(⊙o⊙)!";
        lm_footer_textColor = "#c3c8cb";
        lm_footer_textSize = 14;
        lm_footer_textStyle = Typeface.defaultFromStyle(Typeface.NORMAL);
        lm_footer_text_adjustMargins = false;
        lm_footer_text_adjustMarginsLeft = 0;//dp
        lm_footer_text_adjustMarginsTop = 0;//dp
        lm_footer_text_adjustMarginsRight = 0;//dp
        lm_footer_text_adjustMarginsBottom = 0;//dp
        //*********//底部上拉加載更多完成後的textView//*************//
    }
}

Avtivity:CustomUseInLocalActivity.java

import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.google.gson.Gson;
import com.jidcoo.android.widget.commentview.CommentView;
import com.jidcoo.android.widget.commentview.callback.CustomCommentItemCallback;
import com.jidcoo.android.widget.commentview.callback.CustomReplyItemCallback;
import com.jidcoo.android.widget.commentview.callback.OnCommentLoadMoreCallback;
import com.jidcoo.android.widget.commentview.callback.OnItemClickCallback;
import com.jidcoo.android.widget.commentview.callback.OnPullRefreshCallback;
import com.jidcoo.android.widget.commentview.callback.OnReplyLoadMoreCallback;
import com.jidcoo.android.widgettest.R;
import com.jidcoo.android.widgettest.simple.LocalServer;
import java.lang.ref.WeakReference;

/**
 * 對於CommentView的自定義數據類型和佈局的使用實例(使用本地測試數據)
 * 使用自定義樣式配置器,自定義數據模型,自定義佈局
 * <u>注意:使用自定義數據類型就必須自定義佈局實現,不然會拋出數據模型的java.lang.ClassCastException異常</u><br></br>
 * @author Jidcoo
 */
public class CustomUseInLocalActivity extends AppCompatActivity {
    private CommentView commentView;
    private Gson gson;
    private LocalServer localServer;

    //
    private static class ActivityHandler extends Handler {
        private final WeakReference<CustomUseInLocalActivity> mActivity;
        public ActivityHandler(CustomUseInLocalActivity activity) {
            mActivity = new WeakReference<CustomUseInLocalActivity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            CustomUseInLocalActivity activity = mActivity.get();
            if (activity != null) {
                switch (msg.what){
                    case 1:
                        //commentView.loadFailed(true);//實際網絡請求中若是加載失敗調用此方法
                        activity.commentView.loadComplete(activity.gson.fromJson((String)msg.obj,CustomCommentModel.class));
                        break;
                    case 2:
                        //commentView.refreshFailed();//實際網絡請求中若是加載失敗調用此方法
                        activity.commentView.refreshComplete(activity.gson.fromJson((String)msg.obj, CustomCommentModel.class));
                        break;
                    case 3:
                        //commentView.loadFailed();//實際網絡請求中若是加載失敗調用此方法
                        activity.commentView.loadMoreComplete(activity.gson.fromJson((String)msg.obj,CustomCommentModel.class));
                        break;
                    case 4:
                        //commentView.loadMoreReplyFailed();//實際網絡請求中若是加載失敗調用此方法
                        activity.commentView.loadMoreReplyComplete(activity.gson.fromJson((String)msg.obj,CustomCommentModel.class));
                        break;
                }
            }
        }
    }
    private final ActivityHandler activityHandler = new ActivityHandler(this);
    //

     @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.custom_use);
        gson=new Gson();
        localServer=new LocalServer(this,"api2");
        commentView=findViewById(R.id.commentView);
         //設置空視圖
         //commentView.setEmptyView(view);
         //設置錯誤視圖
         //commentView.setErrorView(view);
         //添加控件頭佈局
        // commentView.addHeaderView();
        commentView.setViewStyleConfigurator(new CustomViewStyleConfigurator(this));

        commentView.callbackBuilder()
                //自定義評論佈局(必須使用ViewHolder機制)--CustomCommentItemCallback<C> 泛型C爲自定義評論數據類
                .customCommentItem(new CustomCommentItemCallback<CustomCommentModel.CustomComment>() {
                    @Override
                    public View buildCommentItem(int groupPosition, CustomCommentModel.CustomComment comment, LayoutInflater inflater, View convertView, ViewGroup parent) {
                        //使用方法就像adapter裏面的getView()方法同樣
                        final CustomCommentViewHolder holder;
                        if(convertView==null){
                            //使用自定義佈局
                            convertView=inflater.inflate(R.layout.custom_item_comment,parent,false);
                            holder=new CustomCommentViewHolder(convertView);
                            //必須使用ViewHolder機制
                            convertView.setTag(holder);
                        }else {
                            holder= (CustomCommentViewHolder) convertView.getTag();
                        }
                        holder.prizes.setText("100");
                        holder.userName.setText(comment.getPosterName());
                        holder.comment.setText(comment.getData());
                        return convertView;
                    }
                })
                //自定義評論佈局(必須使用ViewHolder機制)
                // 而且自定義ViewHolder類必須繼承自com.jidcoo.android.widget.commentview.view.ViewHolder
                // --CustomReplyItemCallback<R> 泛型R爲自定義回覆數據類
                .customReplyItem(new CustomReplyItemCallback<CustomCommentModel.CustomComment.CustomReply>() {
                    @Override
                    public View buildReplyItem(int groupPosition, int childPosition, boolean isLastReply, CustomCommentModel.CustomComment.CustomReply reply, LayoutInflater inflater, View convertView, ViewGroup parent) {
                        //使用方法就像adapter裏面的getView()方法同樣
                        //此類必須繼承自com.jidcoo.android.widget.commentview.view.ViewHolder,不然報錯
                        CustomReplyViewHolder holder=null;
                        //此類必須繼承自com.jidcoo.android.widget.commentview.view.ViewHolder,不然報錯
                        if(convertView==null){
                            //使用自定義佈局
                            convertView=inflater.inflate(R.layout.custom_item_reply,parent,false);
                            holder=new CustomReplyViewHolder(convertView);
                            //必須使用ViewHolder機制
                            convertView.setTag(holder);
                        }else {
                            holder= (CustomReplyViewHolder) convertView.getTag();
                        }
                        holder.userName.setText(reply.getReplierName());
                        holder.reply.setText(reply.getData());
                        holder.prizes.setText("100");
                        return convertView;
                    }
                })
                //下拉刷新回調
                .setOnPullRefreshCallback(new MyOnPullRefreshCallback())
                //評論、回覆Item的點擊回調(點擊事件回調)
                .setOnItemClickCallback(new MyOnItemClickCallback())
                //回覆數據加載更多回調(加載更多回復)
                .setOnReplyLoadMoreCallback(new MyOnReplyLoadMoreCallback())
                //上拉加載更多回調(加載更多評論數據)
                .setOnCommentLoadMoreCallback(new MyOnCommentLoadMoreCallback())
                //設置完成後必須調用CallbackBuilder的buildCallback()方法,不然設置的回調無效
                .buildCallback();
         load(1,1);
    }


    private void load(int code,int handlerId){
        localServer.get(code,activityHandler,handlerId);
    }


    /**
     * 下拉刷新回調類
     */
    class MyOnPullRefreshCallback implements OnPullRefreshCallback {

        @Override
        public void refreshing() {
            load(1,2);


        }

        @Override
        public void complete() {
            //加載完成後的操做
        }

        @Override
        public void failure(String msg) {

        }
    }




    /**
     * 上拉加載更多回調類
     */
    class MyOnCommentLoadMoreCallback implements OnCommentLoadMoreCallback {

        @Override
        public void loading(int currentPage, int willLoadPage, boolean isLoadedAllPages) {
            //由於測試數據寫死了,因此這裏的邏輯也是寫死的
            if (!isLoadedAllPages){
                if(willLoadPage==2){
                    load(2,3);
                }else if(willLoadPage==3){
                    load(3,3);
                }
            }
        }

        @Override
        public void complete() {
            //加載完成後的操做
        }

        @Override
        public void failure(String msg) {
        }
    }

    /**
     * 回覆加載更多回調類
     */
    class MyOnReplyLoadMoreCallback implements OnReplyLoadMoreCallback<CustomCommentModel.CustomComment.CustomReply> {


        @Override
        public void loading(CustomCommentModel.CustomComment.CustomReply reply, int willLoadPage) {
                if(willLoadPage==2){
                    load(5,4);
                }else if(willLoadPage==3){
                    load(6,4);
            }
        }

        @Override
        public void complete() {

        }

        @Override
        public void failure(String msg) {

        }
    }

    /**
     * 點擊事件回調
     */
    class MyOnItemClickCallback implements OnItemClickCallback<CustomCommentModel.CustomComment, CustomCommentModel.CustomComment.CustomReply> {


        @Override
        public void commentItemOnClick(int position, CustomCommentModel.CustomComment comment, View view) {
              Toast.makeText(CustomUseInLocalActivity.this,"你點擊的評論:"+comment.getData(),Toast.LENGTH_SHORT).show();
        }

        @Override
        public void replyItemOnClick(int c_position, int r_position, CustomCommentModel.CustomComment.CustomReply reply, View view) {
            Toast.makeText(CustomUseInLocalActivity.this,"你點擊的回覆:"+reply.getData(),Toast.LENGTH_SHORT).show();
        }
    }

}

3.四、總結

以上就是控件的使用方法和具體使用例子,更多用法能夠查看測試應用的源碼中的例子。到這裏,文章就差很少要結尾了。

4、後話

哈哈,有人可能會問爲何要作這個控件呀?

由於前一段時間作項目的時候須要開發文章的評論功能,而後須要用到相似的控件,原本想在網上找找有沒有開源的,可是全網搜遍之後沒有發現合適的。因此就本身手動寫了一個。

可是我用在項目中的是這個開源的CommentView再進行深度優化和深度定製過的。也就是說目前這個開源出來的版本實際上是比較通用適配版本的,能夠優化的地方還有不少不少不少。同時也存在很多BUG。還有那個測試用例WidgetTest也有不少能夠優化的地方,只是如今沒有太多時間去專門作優化,有空我會再去優化。你們有興趣的能夠一塊兒到github優化這個控件,也能夠提出對這個控件的一些問題、想法以及可優化的點。哈哈,歡迎你們一塊兒來把這個開源控件庫作的更好更強大。

後面有時間的話,這個開源控件庫的安排:
一、優化
二、優化
三、擴大自定義開發的範圍
四、結合市面上的APP評論模塊開發更多通用的樣式框架
五、添加更多功能(嵌套滑動等等更多實用功能)
六、更多的往架構爲一個輪子發展,成爲可以適配絕大多數需求的控件庫
……
……
六、優化

有興趣、有時間的能夠一塊兒加入這個項目的完善,很是歡迎你們呀!!

順便提一下,目前用在我工做的項目中的版本顯示10000+的數據性能都挺高的,在大數據量時內存開銷也穩定在一個相對的值。畢竟是在這個開源版本基礎上作了不少的深度優化。因此說,因此說,若是使用的場景是數據量比較大的狀況下,優化是頗有必要的哦,否則數據量比較大很容易OOM。

5、關於做者

我叫Jidcoo,一名理工男,一個專一於Java後端同時又熱愛Android開發的花心大蘿蔔。我熱愛閱讀、熱愛算法、熱愛分享……喜歡交流、喜歡團隊合做。

有任何有關這個項目或者有任何其餘問題、想法,均可以POST我:

個人郵箱:jidcoo@163.com

個人GitHub: https://github.com/Jidcoo/

相關文章
相關標籤/搜索