Android 5.X新特性之RecyclerView基本解析及無限復用

来源:http://www.cnblogs.com/guanmanman/archive/2016/11/22/6088056.html
-Advertisement-
Play Games

說到RecyclerView,相信大家都不陌生,它是我們經典級ListView的升級版,升級後的RecyclerView展現了極大的靈活性。同時內部直接封裝了ViewHolder,不用我們自己定義ViewHolder就能實現item的回收和復用功能。當然它肯定不止這些好處,比如我們可以自定義分割線,... ...


說到RecyclerView,相信大家都不陌生,它是我們經典級ListView的升級版,升級後的RecyclerView展現了極大的靈活性。同時內部直接封裝了ViewHolder,不用我們自己定義ViewHolder就能實現item的回收和復用功能。當然它肯定不止這些好處,比如我們可以自定義分割線,可以更加方便的實現列表的佈局方式等等。雖說我們自己在第一次使用時,會比使用listView和gridView稍微的複雜一些,需要自定義的也多了一點,但是它卻更好的體現了靈活性,可以隨自己的喜好來隨便的定義,當然最主要的是能更好的復用,只需一次的定義,卻可隨處的復用。

下麵,我們來好好的學習下它的使用。

首先,我們要是用RecyclerView必須引入support-V7包,拿android studio來舉例:

先打開File->選擇Project Structure,之後在左邊Modules選擇你的項目,然後在點擊右邊的Dependencies,然後點擊綠色的+號選擇添加Library,然後找到recyclerview-v7雙擊加入到依賴庫中。然後可以在build.gradle中查看:

dependencies {
    compile fileTree(include: ['*.jar'], dir: 'libs')
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.3.0'
    compile 'com.android.support:recyclerview-v7:23.3.0'
}

可以找dependencies 中找到對recyclerview的支持,則說明添加成功,如果還沒有的,可以clean下自己的工程。

接下來,我們就進入Recyclerview的學習。RecyclerView的學習目標就是以下四個方法,把以下四個方法完全的掌握了,也就真正的掌握了RecyclerView。

RecyclerView.setAdapter:用來設置adapter,顯示數據
RecyclerView.setLayoutManager :用來設置顯示佈局的,目前系統給出三種佈局,分別是垂直,水平和瀑布流式佈局
RecyclerView.setItemAnimator :用來設置顯示動畫的
RecyclerView.addItemDecoration :用來設置列表分割線的

接下來我們就學習怎麼使用以上四個方法來真正掌握Recyclerview的使用。要使用Recyclerview,我們必須先定義一個類(CustomRecyclerAdapter)並繼承Recyclerview.Adapter,且實現它裡面的方法,代碼如下:

public class CustomRecyclerAdapter extends RecyclerView.Adapter<CustomRecyclerAdapter.ViewHolderHelper>{

    @Override
    public ViewHolderHelper onCreateViewHolder(ViewGroup parent, int viewType) {
        return null;
    }
    @Override
    public void onBindViewHolder(ViewHolderHelper holder, int position) {

    }
    @Override
    public int getItemCount() {
        return 0;
    }

    public class ViewHolderHelper extends RecyclerView.ViewHolder{

        public ViewHolderHelper(View itemView) {
            super(itemView);
        }
    }
}

在我們還沒正式開始使用之前,先大體上瞭解下上面三個方法是做什麼的:

A. onCreateViewHolder()方法:該方法就是將佈局文件轉化為View並傳遞給RecyclerView封裝好的ViewHolder。

B. onBindViewHolder()方法:該方法將會在固定的位置上把ViewHolder里的itemView數據映射在item中。

C. getItemCount()方法:該方法和listView中的getCount()一樣,都是返回Item的個數。

瞭解了這三個方法,我們來先實現最簡單的應用,把我們的數據顯示在app中。

首先,我們創建一個佈局文件recycler_view.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:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </android.support.v7.widget.RecyclerView>

</LinearLayout>

創建RecycerActivity用來載入佈局文件:

public class RecycerActivity extends Activity {
    private RecyclerView mRecyclerView;
    private List<String> mData;
    private CustomRecyclerAdapter mCustomRecyclerAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recycer_view);
        mRecyclerView = (RecyclerView) findViewById(R.id.recycler_view);
        initData();
        //線性佈局管理器
        recyclerViewLayoutManager = new LinearLayoutManager(this);
        //設置佈局管理器
        mRecyclerView.setLayoutManager(recyclerViewLayoutManager);
        //設置顯示動畫
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        //設置adapter
        mCustomRecyclerAdapter = new CustomRecyclerAdapter(mData);
        mRecyclerView.setAdapter(mCustomRecyclerAdapter);

    }
    private void initData() {
        mData = new ArrayList<String>();
        for(int i = 0; i < 10; i++){
            mData.add("第"+i+"item");
        }
    }
}

經過修改的CustomRecyclerAdapter 如下,

public class CustomRecyclerAdapter extends RecyclerView.Adapter<CustomRecyclerAdapter.ViewHolderHelper>{

    private List<String> mData;
    public CustomRecyclerAdapter(List<String> data) {
        mData = data;
    }

    @Override
    public ViewHolderHelper onCreateViewHolder(ViewGroup parent, int viewType) {
        //onCreateViewHolder方法就是將佈局文件轉化為View並傳遞給RecyclerView封裝好的ViewHolder
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_view, parent, false);
        return new ViewHolderHelper(view);
    }

    @Override
    public void onBindViewHolder(ViewHolderHelper holder, int position) {
        holder.textView.setText(mData.get(position));
    }

    @Override
    public int getItemCount() {
        return mData.size();
    }
}

item_view.xml佈局文件

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">
    <TextView
        android:id="@+id/tv_item"
        android:layout_toRightOf="@+id/iv_item"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:text="I am item view"/>

</RelativeLayout>

好,基礎工作已做足,我們先來看看效果吧

這裡寫圖片描述

已經顯示出來了,不過,大家看著是不是很彆扭呢,連個分割線也沒有,還不如listView呢,別急,我們在上面也提到過,RecyclerView給了我們最大的發揮自由度,它本身並沒有給定列表的分割線,這是需要我們自己定義的。由此我們來定義自己的分割線。自定義分割線是需要我們繼承RecyclerView.ItemDecoration類,並實現它的onDraw()方法。請看代碼:

public class DividerItemDecoration extends RecyclerView.ItemDecoration{

    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
    private int mOrientation;
    private Context mContext;
    private TextPaint mTextPaint;
    private float listDividerSize = 2;
    private int listDividerColor;
    public DividerItemDecoration(Context context,int orientation){
        mContext = context;
        mTextPaint = new TextPaint();
        mTextPaint.setColor(Color.RED);
        setOrientation(orientation);
    }
    public void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }
    @Override
    public void onDraw(Canvas c, RecyclerView parent) {
        super.onDraw(c, parent);
        if(mOrientation == HORIZONTAL_LIST){
            drawHorizontal(c, parent);
        }else{
            drawVertical(c, parent);
        }
    }
    /**
     * 繪製垂直分割線
     * @param c
     * @param parent
     */
    private void drawVertical(Canvas c, RecyclerView parent) {
        //分割線的左邊界 = 子View的左padding值
        int rectLeft = parent.getPaddingLeft();
        //分割線的右邊界 = 子View的寬度 - 子View的右padding值
        int rectRight = parent.getWidth() - parent.getPaddingRight();
        int childCount = parent.getChildCount();
        for(int i = 0; i < childCount; i ++){
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            // 分割線的top = 子View的底部 + 子View的margin值
            int rectTop = child.getBottom() + layoutParams.bottomMargin;
            // 分割線的bottom = 分割線的top + 分割線的高度
            float rectBottom = rectTop + listDividerSize;
            c.drawRect(rectLeft,rectTop,rectRight,rectBottom,mTextPaint);
        }
    }

    /**
     * 繪製水平分割線
     * @param c
     * @param parent
     */
    private void drawHorizontal(Canvas c, RecyclerView parent) {
        //分割線的上邊界 = 子View的上padding值
        int rectTop = parent.getPaddingTop();
        //分割線的下邊界 = 子View的高度 - 子View的底部padding值
        int rectBottom = parent.getHeight() - parent.getPaddingBottom();
        int childCount = parent.getChildCount();
        for(int i = 0; i < childCount; i ++){
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            //分割線的Left = 子View的右邊界 + 子View的左margin值
            int rectLeft = child.getRight() + layoutParams.rightMargin;
            //分割線的right = 分割線的Left + 分割線的寬度
            float rectRight = rectLeft + listDividerSize;
            c.drawRect(rectLeft,rectTop,rectRight,rectBottom,mTextPaint);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        if(mOrientation == VERTICAL_LIST){
            outRect.set(0,0,0,(int)listDividerSize);
        } else{
            outRect.set(0,0,(int)listDividerSize,0);
        }
    }
}

代碼很好理解,這裡考慮了兩個情況,分別是垂直和水平的佈局,然後再ondraw()裡面計算出四角邊值,最後直接繪製一個矩形即可。

在RecycerActivity 中的onCreate中添加上一句
mRecyclerView.addItemDecoration(new DividerItemDecoration(this,DividerItemDecoration.VERTICAL_LIST));
現在再來看看效果。
這裡寫圖片描述

現在已顯示出來了,分割線也出來了,在這裡只列出了垂直方向的佈局,就不再列出其他樣式的佈局代碼了,伙伴們可自行寫寫看。

或許有經驗的小伙伴們已經知道我們的RecyclerView自己是沒有實現點擊事件的,這裡需要我們來根據業務的需求自己來實現。這裡我們利用事件回調機制來完成事件的觸發。

首先我們需要在CustomRecyclerAdapter中定義一個介面,併在其中定義兩個可用的事件方法,如下:

public interface OnItemClickListener{
        void onItemClickListener();
        void onLongItemClickListener();
    }

這裡提供了用於點擊和長按的事件方法,接下來我們需要對外暴露該介面用於被調用

 public void setOnClickItemListener(OnItemClickListener onItemClickListener){
        mOnItemClickListener = onItemClickListener;
 }

然後我們可以在ViewHolderHelper 做如下的修改:

public class ViewHolderHelper extends RecyclerView.ViewHolder{

        private TextView textView;
        public ViewHolderHelper(View itemView) {
            super(itemView);
            textView = (TextView)itemView.findViewById(R.id.tv_item);
            textView.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    mOnItemClickListener.onItemClickListener();
                }
            });
            textView.setOnLongClickListener(new View.OnLongClickListener() {
                @Override
                public boolean onLongClick(View v) {
                    mOnItemClickListener.onLongItemClickListener();
                    return false;
                }
            });
        }
    }

首先得到我們itemView中的textView對應的Id,然後為textView添加事件,但是只是添加卻並不實現,它的用意是誰調用誰實現。

最後在我們的RecycerActivity中添加如下代碼,請看:

mCustomRecyclerAdapter.setOnClickItemListener(new CustomRecyclerAdapter.OnItemClickListener() {
            @Override
            public void onItemClickListener() {
                Toast.makeText(getContext(),"單擊",Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onLongItemClickListener() {
                Toast.makeText(getContext(),"長單擊",Toast.LENGTH_SHORT).show();
            }
        });

下麵在來看看是否可以實現事件的觸發了呢?

這裡寫圖片描述

ok,學到這裡大家至少對RecylerView有了一個初步的認識,但是我們一想,這樣寫的話肯定達不到我們標題所說的無限復用,甚至連復用也遙不可及,是的,這樣寫是不可能完成復用的,接下來我們一步一步的慢慢調整,讓它可以支持一次編寫N次復用,達到極大多數的重覆使用,即使不符合需求,我們也只需要修改丁點即可滿足需求,這是我們的目標,接下來一步一步的實現。

首先,我們先整理下,看看可以調整哪些目標能逐步的實現重覆使用的目標:

1. 數據類型:我們在使用List集合時,是無法固定類型的,有可能是String,int等等類型,所以我們不應該固定為哪一種類型。

2. 在onCreateViewHolder方法中,它需要映射一個佈局文件並轉化為View或是一個自定義View傳遞給RecyclerView封裝好的ViewHolder,為了可以達到復用,所以我們就不可以在此直接映射佈局文件。

3. 在onBindViewHolder方法中也不應該直接為itemView設置屬性,如上面的:holder.textView.setText(mData.get(position));

4. 我們不應該在ViewHolder的構造方法中直接獲取我們的itemView,並給它添加觸發事件

以上幾個是我們能很直觀的得到的能重構的問題所在,至於其他的不容易想到的我們再重構的時候慢慢講解。現在我們逐一的解決上面的問題,使我們能更達到重覆使用的目的。

1,針對數據類型的不一致,我們可以根據具體的使用場景利用泛型進行傳遞到Adapter中,比如:我們再定義CustomRecyclerAdapter時使用泛型,讓調用者傳遞過來它所擁有的類型,這樣我們就可以不用考慮類型的不一致了。請看下麵片段代碼

public class CustomRecyclerAdapter<T> extends RecyclerView.Adapter<CustomViewHolderHelper>{
    private Context mContext;
    private List<T> mData;
    private CustomOnItemClickListener mOnItemClickListener;
    public CustomRecyclerAdapter(Context context, List<T> data) {
        mContext = context;
        mData = data;
    }
 }
 ...

RecycerActivity中在調用時可以這樣使用:
mCustomRecyclerAdapter = new CustomRecyclerAdapter

這樣就可以把類型給確定下來了,同時也解決了問題1的復用。

2 , 在onCreateViewHolder方法中,和問題一的解決方案是一樣的,我們把需要的itemView給傳遞過去而不是固定寫死在方法中

public class CustomRecyclerAdapter<T> extends RecyclerView.Adapter<CustomViewHolderHelper>{
    private Context mContext;
    private List<T> mData;
    protected int mLayoutResId;
    private CustomOnItemClickListener mOnItemClickListener;
    public CustomRecyclerAdapter(Context context, int layoutResId, List<T> data) {
        mContext = context;
        mLayoutResId = layoutResId;
        mData = data;
    }

    /**
     * Called when RecyclerView needs a new ViewHolder of the given type to represent
     * an item.
     * 當 RecyclerView 依據給出的類型需要一個新的 ViewHolder 去展示一個 item 時,該方法將會被調用
     *
     * 這個給出的類型是在 getItemViewType返回的,預設返回 0*
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public CustomViewHolderHelper onCreateViewHolder(ViewGroup parent, int viewType) {
        //onCreateViewHolder方法就是將佈局文件轉化為View並傳遞給RecyclerView封裝好的ViewHolder
        View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutResId, parent, false);
        return new CustomViewHolderHelper(view);
    }
}

RecycerActivity中在調用時可以這樣使用:
mCustomRecyclerAdapter = new CustomRecyclerAdapter

3 , 在onBindViewHolder方法中也不應該直接為itemView設置屬性,我們可以在這裡記錄itemView的position,並給它設置監聽事件,更重要的我們這這裡可以創建一個抽象方法,讓調用者自己去實現業務邏輯。請看代碼:

public abstract class CustomRecyclerAdapter<T> extends RecyclerView.Adapter<CustomViewHolderHelper>
                                        implements View.OnClickListener,View.OnLongClickListener{
    private Context mContext;
    private List<T> mData;
    protected int mLayoutResId;
    private CustomOnItemClickListener mOnItemClickListener;
    public CustomRecyclerAdapter(Context context, int layoutResId, List<T> data) {
        mContext = context;
        mLayoutResId = layoutResId;
        mData = data;
    }

    /**
     * Called when RecyclerView needs a new ViewHolder of the given type to represent
     * an item.
     * 當 RecyclerView 依據給出的類型需要一個新的 ViewHolder 去展示一個 item 時,該方法將會被調用
     *
     * 這個給出的類型是在 getItemViewType返回的,預設返回 0*
     * @param parent
     * @param viewType
     * @return
     */
    @Override
    public CustomViewHolderHelper onCreateViewHolder(ViewGroup parent, int viewType) {
        //onCreateViewHolder方法就是將佈局文件轉化為View並傳遞給RecyclerView封裝好的ViewHolder
        View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutResId, parent, false);
        return new CustomViewHolderHelper(view);
    }

    /**
     * Called by RecyclerView to display the data at the specified position. This method should
     * update the contents of the ViewHolder#itemView to reflect the item at the given position.
     *
     * RecyclerView 將要在特殊的位置上顯示數據時,該方法將被調用。該方法將會在固定的位置上
     *  把ViewHolder里的itemView數據映射在item中。
     * @param holder
     * @param position
     */
    @Override
    public void onBindViewHolder(CustomViewHolderHelper holder, int position) {
        //把每一個itemView設置一個標簽,方便以後根據標簽獲取到該itemView以便做其他事項,比如點擊事件
        holder.itemView.setTag(position);
        holder.itemView.setOnClickListener(this);
        holder.itemView.setOnLongClickListener(this);
        T itemData = mData.get(position);
        displayContents(holder,itemData);
    }

    /**
     *用來在holder中設置每個ItemView的顯示數據
     * 設定為抽象方法,是為:自己本身並不實現,誰使用誰設置
     * @param holder
     * @param itemData
     */
    protected abstract void displayContents(CustomViewHolderHelper holder, T itemData);
}

上面註解也寫的很清楚,相信大家一看就明白,至於為什麼可以直接使用holder.itemView來獲取每個itemView是因為在onCreateViewHolder()方法中,我們返回了一個新的對象引用,這個對象的構造方法中使用super(itemView);把我們的itemView傳遞到了ViewHolder中,請看它的源碼構造方法:

 public ViewHolder(View itemView) {
     if (itemView == null) {
         throw new IllegalArgumentException("itemView may not be null");
     }
     this.itemView = itemView;
 }

因此我們可以在onBindViewHolder()方法中,直接使用holder.itemView來獲取itemView。那麼接下來,在RecycerActivity中,我們這樣使用:

 mCustomRecyclerAdapter = new CustomRecyclerAdapter<String>(this, R.layout.item_view, mData) {
            @Override
            protected void displayContents(CustomViewHolderHelper holder, String itemData) {
                holder.setText(R.id.tv_item,itemData);
            }
 };

當然你必須也得在ViewHolder中定義相應的方法,如:

public class CustomViewHolderHelper extends RecyclerView.ViewHolder{

    private SparseArray<View> views;
    public CustomViewHolderHelper(View itemView) {
        super(itemView);
        views = new SparseArray<View>();
    }
    private <T extends View> T converToViewFromId(int resId) {
        View view = views.get(resId);
        if(view == null){
            view = itemView.findViewById(resId);
        }
        views.put(resId,view);
        return (T)view;
    }

    public CustomViewHolderHelper setText(int resId, String value){
        TextView itemView = converToViewFromId(resId);
        if (TextUtils.isEmpty(value)) {
            itemView.setText("");
        } else {
            itemView.setText(value);
        }
        return  this;
    }
}

ok,這樣我們連第四個問題也一併解決了,看下效果吧,完全的一樣,這樣我們就實現的重覆使用,但是有人會有疑問,這裡也就只能使用TextView啊,其實已經在ViewHolder中給出了答案,大家只需要在添加相對應的方法即可,比如

public CustomViewHolderHelper setImageResource(int viewId, int imageResId) {
        ImageView view = converToViewFromId(viewId);
        view.setImageResource(imageResId);
        return this;
}
public CustomViewHolderHelper setOnClickListener(int viewId, View.OnClickListener listener) {
        View view = converToViewFromId(viewId);
        view.setOnClickListener(listener);
        return this;
}

上面我又添加了兩個方法,用於點擊事件和載入圖片的ImageView,下麵我們再把itemView佈局文件修改下:

item_view.xml佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/iv_item"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/tv_item"
        android:layout_toRightOf="@+id/iv_item"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:text="I am item view"/>
    <Button
        android:id="@+id/btn_item"
        android:layout_toRightOf="@+id/iv_item"
        android:layout_below="@+id/tv_item"
        android:layout_width="wrap_content"
        android:text="點我"
        android:layout_height="wrap_content" />
</RelativeLayout>

RecycerActivity中在調用時可以這樣使用:

mCustomRecyclerAdapter = new CustomRecyclerAdapter<String>(this, R.layout.item_view, mData) {
            @Override
            protected void displayContents(CustomViewHolderHelper holder, String itemData) {
                holder.setText(R.id.tv_item,itemData)
                      .setImageResource(R.id.iv_item,R.mipmap.ic_launcher)
                      .setOnClickListener(R.id.btn_item, new View.OnClickListener() {
                          @Override
                          public void onClick(View v) {
                              Toast.makeText(RecycerActivity.this,"您單擊了按鈕",Toast.LENGTH_SHORT).show();
                          }
                      });
            }
        };

ok,來看下效果吧
這裡寫圖片描述

是不是可以了呢,這樣我們就可以完全的一次定義N次復用了,每次使用只需要更換不同的佈局文件即可而不需要再次編寫代碼,學會了吧。

其實我們這節課主要講解的RecyclerView.setAdapter的內容,其他的三個我們並沒有詳細的介入,我們會再以後的博文中陸續的講解。

好了,今天就講到這裡吧,祝大家學習愉快。

更多資訊請關註微信平臺,有博客更新會及時通知。愛學習愛技術。

這裡寫圖片描述


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、三個對話框 1、alert("提示信息") 彈出只帶有一個確定按鈕的對話框2、confirm("提示信息") 彈出有確定和取消按鈕的對話框3、prompt("提示信息",預設值) 可輸入內容的對話框,返回null 二、數據類型 1、var通用類型2、數據類型轉換(1)將變數轉為整數類型parse ...
  • 工廠模式:無法識別對象 function createObject(name, age) { //集中實例化的函數 var obj = new Object(); obj.name = name; obj.age = age; obj.run = function () { return this. ...
  • Ionic2使用了近似原生App的頁面導航方式,並不支持Angular2的路由。這種方式在開發本地App的時候比較方便,但如果要用來開發純Web頁面就有點問題了,這種情況下Angular2的router可以提供更靈活的配置。比如在首頁是一個Tabs頁面的情況下,如何控制用戶看到的第一項Tab?預設情 ...
  • 1、插件下載地址:http://www.chromein.com/search_livereload_1.html 2、谷歌瀏覽器啟用改插件 3、sublime 安裝livereload插件,安裝方法與其他插件一樣 4、點擊瀏覽器改小圖標邊實心 這樣就可以直接修改完html或者css保存,不需要刷新 ...
  • ...
  • <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, mi ...
  • <!-- ////////////////////////// 請款列表模板 開始--><div id="rtlist-temple"> <div class="list-box m-top-20"> <div class="list-title font-size-26 clearfix"> <s ...
  • 優美整潔的代碼,基本都需要遵循以下的幾大基本原則. 1.單一職責原則SRP (Single Responsibility Principle) 類,模塊,方法應該有且僅有一條修改的理由.也就是說,其僅僅負責一個功能,只有這個功能發生變化時,才需要修改它. 舉個慄子:人類 有 吃飯的方法,那麼這個方法 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...