RecyclerView使用大全

来源:http://www.cnblogs.com/yishaochu/archive/2017/01/05/6254089.html
-Advertisement-
Play Games

RecylerView介紹 RecylerView是support v7包中的新組件,是一個強大的滑動組件,與經典的ListView相比,同樣擁有item回收復用的功能,這一點從它的名字recylerview即回收view也可以看出。官方對於它的介紹則是:RecyclerView 是 ListVie ...


RecylerView介紹

RecylerView是support-v7包中的新組件,是一個強大的滑動組件,與經典的ListView相比,同樣擁有item回收復用的功能,這一點從它的名字recylerview即回收view也可以看出。官方對於它的介紹則是:RecyclerView 是 ListView 的升級版本,更加先進和靈活。RecyclerView通過設置LayoutManager,ItemDecoration,ItemAnimator實現你想要的效果。

  • 使用LayoutManager來確定每一個item的排列方式。
  • 使用ItemDecoration自己繪製分割線,更靈活
  • 使用ItemAnimator為增加或刪除一行設置動畫效果。

註意

新建完項目,需要在app/build.gradle增加RecylerView依賴,不然找不到RecyclerView類

compile 'com.android.support:recyclerview-v7:23.1.0'

RecylerView簡單的Demo

我們來看activity代碼,跟ListView寫法差不多,只是這邊多設置了佈局管理器。

public class LinearLayoutActivity extends AppCompatActivity {
    private RecyclerView recyclerView;
    private RecyclerViewAdapter adapter;
    private List<String> datas;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.recycler_main);

        initData();

        recyclerView= (RecyclerView) findViewById(R.id.recyclerview);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));//設置佈局管理器
        recyclerView.addItemDecoration(new DividerItemDecoration(this));
        recyclerView.setAdapter(adapter=new RecyclerViewAdapter(this,datas));
    }

    private void initData(){
        datas=new ArrayList<>();
        for(int i=0;i<100;i++){
            datas.add("item:"+i);
        }
    }
}

activity對應的佈局文件:recycler_main.xml

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

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

adapter相對ListView來說變化比較大的。把ViewHolder邏輯封裝起來了,代碼相對簡單一些。

  • 需要繼承RecyclerView.Adapter,重寫三個方法
  • MyViewHolder需要繼承RecyclerView.ViewHolder

    public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder>{
    private List<String> datas;
    private LayoutInflater inflater;
    
    public  RecyclerViewAdapter(Context context,List<String> datas){
        inflater=LayoutInflater.from(context);
        this.datas=datas;
    }
    
    //創建每一行的View 用RecyclerView.ViewHolder包裝
    @Override
    public RecyclerViewAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView=inflater.inflate(R.layout.recycler_item,null);
        return new MyViewHolder(itemView);
    }
    
    //給每一行View填充數據
    @Override
    public void onBindViewHolder(RecyclerViewAdapter.MyViewHolder holder, int position) {
        holder.textview.setText(datas.get(position));
    }
    
    //數據源的數量
    @Override
    public int getItemCount() {
        return datas.size();
    }
    
    class MyViewHolder extends RecyclerView.ViewHolder{
        private TextView textview;
    
        public MyViewHolder(View itemView) {
            super(itemView);
            textview= (TextView) itemView.findViewById(R.id.textview);
        }
    }
    }

我們來看看效果圖:

RecylerView基本使用

RecyclerView增加分隔線

RecyclerView是沒有android:divider跟android:dividerHeight屬性的,如果我們需要分割線,就只能自己動手去實現了。

  • 需要繼承ItemDecoration類,實現onDraw跟getItemOffsets方法。
  • 調用RecyclerView的addItemDecoration方法。

我們先寫一個DividerItemDecoration類,繼承RecyclerView.ItemDecoration,在getItemOffsets留出item之間的間隔,然後就會調用onDraw方法繪製(onDraw的繪製優先於每一行的繪製)

public class DividerItemDecoration extends RecyclerView.ItemDecoration{
    /*
    * RecyclerView的佈局方向,預設先賦值 為縱向佈局
    * RecyclerView 佈局可橫向,也可縱向
    * 橫向和縱向對應的分割線畫法不一樣
    * */
    private int mOrientation = LinearLayoutManager.VERTICAL;

    private int mItemSize = 1;//item之間分割線的size,預設為1

    private Paint mPaint;//繪製item分割線的畫筆,和設置其屬性

    public DividerItemDecoration(Context context) {
        this(context,LinearLayoutManager.VERTICAL,R.color.colorAccent);
    }

    public DividerItemDecoration(Context context, int orientation) {
        this(context,orientation, R.color.colorAccent);
    }

    public DividerItemDecoration(Context context, int orientation, int dividerColor){
        this(context,orientation,dividerColor,1);
    }

    /**
     * @param context
     * @param orientation 繪製方向
     * @param dividerColor 分割線顏色 顏色資源id
     * @param mItemSize 分割線寬度 傳入dp值就行
     */
    public DividerItemDecoration(Context context, int orientation, int dividerColor, int mItemSize){
        this.mOrientation = orientation;
        if(orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL){
            throw new IllegalArgumentException("請傳入正確的參數") ;
        }
        //把dp值換算成px
        this.mItemSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,mItemSize,context.getResources().getDisplayMetrics());
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        mPaint.setColor(context.getResources().getColor(dividerColor));
    }


    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if(mOrientation == LinearLayoutManager.VERTICAL){
            drawVertical(c,parent) ;
        }else {
            drawHorizontal(c,parent) ;
        }
    }

    /**
     * 繪製縱向 item 分割線
     * @param canvas
     * @param parent
     */
    private void drawVertical(Canvas canvas,RecyclerView parent){
        final int left = parent.getPaddingLeft() ;
        final int right = parent.getMeasuredWidth() - parent.getPaddingRight();
        final int childSize = parent.getChildCount() ;
        for(int i = 0 ; i < childSize ; i ++){
            final View child = parent.getChildAt( i ) ;
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int top = child.getBottom() + layoutParams.bottomMargin ;
            final int bottom = top + mItemSize ;
            canvas.drawRect(left,top,right,bottom,mPaint);
        }
    }

    /**
     * 繪製橫向 item 分割線
     * @param canvas
     * @param parent
     */
    private void drawHorizontal(Canvas canvas,RecyclerView parent){
        final int top = parent.getPaddingTop() ;
        final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom() ;
        final int childSize = parent.getChildCount() ;
        for(int i = 0 ; i < childSize ; i ++){
            final View child = parent.getChildAt( i ) ;
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int left = child.getRight() + layoutParams.rightMargin ;
            final int right = left + mItemSize ;
            canvas.drawRect(left,top,right,bottom,mPaint);
        }
    }

    /**
     * 設置item分割線的size
     * @param outRect
     * @param view
     * @param parent
     * @param state
     */
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if(mOrientation == LinearLayoutManager.VERTICAL){
            outRect.set(0,0,0,mItemSize);//垂直排列 底部偏移
        }else {
            outRect.set(0,0,mItemSize,0);//水平排列 右邊偏移
        }
    }
}

不要忘記調用addItemDecoration方法哦

recyclerView.addItemDecoration(new DividerItemDecoration(this));//添加分割線

重新運行,效果圖:

添加分割線

大家讀到這裡肯定會有一個疑問,這貨比ListView麻煩多了啊,但是google官方為什麼要說是ListView的升級版呢?接下來開始放大招。。。

GridLayoutManager

在RecyclerView中實現不同的列表,只需要切換不同的LayoutManager即可。RecyclerView.LayoutManager跟RecyclerView.ItemDecoration一樣,都是RecyclerView靜態抽象內部類,但是LayoutManager有三個官方寫好的實現類。

  • LinearLayoutManager 線性佈局管理器 跟ListView功能相似
  • GridLayoutManager 網格佈局管理器 跟GridView功能相似
  • StaggeredGridLayoutManager 瀑布流佈局管理器

剛剛我們用的是LinearLayoutManager,現在我們切換到GridLayoutManager,看到下麵這句代碼,有沒有感覺分分鐘切換不同列表顯示。

recyclerView.setLayoutManager(new GridLayoutManager(this,2));

如果要顯示多列或者要縱向顯示就new不同的構造方法,以下代碼縱向顯示4列。當前如果你還需要反方向顯示,把false改成true就可以。

recyclerView.setLayoutManager(new GridLayoutManager(this,4,GridLayoutManager.HORIZONTAL,false));

因為用的是網格佈局,所以呢繪製分割線的代碼需要重新修改一下。網格佈局一行可以有多列,並且最後一列跟最後一行不需要繪製,所以我們得重新創建一個類。
DividerGridItemDecoration.java

public class DividerGridItemDecoration extends RecyclerView.ItemDecoration {
    /*
    * RecyclerView的佈局方向,預設先賦值 為縱向佈局
    * RecyclerView 佈局可橫向,也可縱向
    * 橫向和縱向對應的分割線畫法不一樣
    * */
    private int mOrientation = LinearLayoutManager.VERTICAL;

    private int mItemSize = 1;//item之間分割線的size,預設為1

    private Paint mPaint;//繪製item分割線的畫筆,和設置其屬性

    public DividerGridItemDecoration(Context context) {
        this(context,LinearLayoutManager.VERTICAL,R.color.colorAccent);
    }

    public DividerGridItemDecoration(Context context, int orientation) {
        this(context,orientation, R.color.colorAccent);
    }

    public DividerGridItemDecoration(Context context, int orientation, int dividerColor){
        this(context,orientation,dividerColor,1);
    }

    /**
     * @param context
     * @param orientation 繪製方向
     * @param dividerColor 分割線顏色 顏色資源id
     * @param mItemSize 分割線寬度 傳入dp值就行
     */
    public DividerGridItemDecoration(Context context, int orientation, int dividerColor, int mItemSize){
        this.mOrientation = orientation;
        if(orientation != LinearLayoutManager.VERTICAL && orientation != LinearLayoutManager.HORIZONTAL){
            throw new IllegalArgumentException("請傳入正確的參數") ;
        }
        //把dp值換算成px
        this.mItemSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,mItemSize,context.getResources().getDisplayMetrics());
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        mPaint.setColor(context.getResources().getColor(dividerColor));
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        drawHorizontal(c, parent);
        drawVertical(c, parent);
    }

    private int getSpanCount(RecyclerView parent) {
        // 列數
        int spanCount = -1;
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            spanCount = ((StaggeredGridLayoutManager) layoutManager).getSpanCount();
        }
        return spanCount;
    }

    public void drawHorizontal(Canvas canvas, RecyclerView parent) {
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);
            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int left = child.getLeft() - params.leftMargin;
            final int right = child.getRight() + params.rightMargin + mItemSize;
            final int top = child.getBottom() + params.bottomMargin;
            final int bottom = top + mItemSize;
            canvas.drawRect(left,top,right,bottom,mPaint);
        }
    }

    public void drawVertical(Canvas canvas, RecyclerView parent) {
        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            final View child = parent.getChildAt(i);

            final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            final int top = child.getTop() - params.topMargin;
            final int bottom = child.getBottom() + params.bottomMargin;
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mItemSize;
            canvas.drawRect(left,top,right,bottom,mPaint);
        }
    }

    @Override
    public void getItemOffsets(Rect outRect, int itemPosition,RecyclerView parent) {
        int spanCount = getSpanCount(parent);
        int childCount = parent.getAdapter().getItemCount();

        if (isLastRow(parent, itemPosition, spanCount, childCount)){//如果是最後一行,不需要繪製底部
            outRect.set(0, 0, mItemSize, 0);
        } else if (isLastColum(parent, itemPosition, spanCount, childCount)){// 如果是最後一列,不需要繪製右邊
            outRect.set(0, 0, 0, mItemSize);
        } else {
            outRect.set(0, 0, mItemSize,mItemSize);
        }
    }

    private boolean isLastColum(RecyclerView parent, int pos, int spanCount, int childCount) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            if ((pos + 1) % spanCount == 0){// 如果是最後一列,則不需要繪製右邊
                return true;
            }
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
            if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                if ((pos + 1) % spanCount == 0){// 如果是最後一列,則不需要繪製右邊
                    return true;
                }
            } else {
                childCount = childCount - childCount % spanCount;
                if (pos >= childCount)// 如果是最後一列,則不需要繪製右邊
                    return true;
            }
        }
        return false;
    }

    private boolean isLastRow(RecyclerView parent, int pos, int spanCount, int childCount) {
        RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
        if (layoutManager instanceof GridLayoutManager) {
            childCount = childCount - childCount % spanCount;
            if (pos >= childCount)//最後一行
                return true;
        } else if (layoutManager instanceof StaggeredGridLayoutManager) {
            int orientation = ((StaggeredGridLayoutManager) layoutManager).getOrientation();
            if (orientation == StaggeredGridLayoutManager.VERTICAL){//縱向
                childCount = childCount - childCount % spanCount;
                if (pos >= childCount)//最後一行
                    return true;
            } else{ //橫向
                if ((pos + 1) % spanCount == 0) {//是最後一行
                    return true;
                }
            }
        }
        return false;
    }
}

寫了這兩個畫分割線的類,主流的佈局:線性列表跟網格列表都能展示了。。。趕緊運行代碼看看結果:

網格列表

StaggeredGridLayoutManager

actviity中修改下佈局管理器,大家應該感覺很熟悉了吧~~~

recyclerView.setLayoutManager(new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL));

瀑布流列表一般列的高度是不一致的,為了模擬不同的寬高,數據源我把String類型改成了對象.然後初始化的時候隨機了一個高度.

public class ItemData {
    private String content;//item內容
    private int height;//item高度

    public ItemData() {
    }

    public ItemData(String content, int height) {
        this.content = content;
        this.height = height;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }
}

瀑布流列表沒有添加分割線,給item佈局設置了android:padding屬性。recycler_staggered_item.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:padding="5dp"
    android:layout_width="wrap_content"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/textview"
        android:background="@color/colorAccent"
        android:layout_width="100dp"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:text="122"
        android:textSize="20sp"/>
</FrameLayout>

最後我們在適配器的onBindViewHolder方法中給itemd中的TextView設置一個高度

@Override
public void onBindViewHolder(StaggeredGridAdapter.MyViewHolder holder, int position) {
    ItemData itemData=datas.get(position);
    holder.textview.setText(itemData.getContent());
    //手動更改高度,不同位置的高度有所不同
    holder.textview.setHeight(itemData.getHeight());
}

是不是感覺so easy,趕緊運行看看效果:

瀑布流效果

添加header跟footer

RecyclerView添加頭部跟底部是沒有對應的api的,但是我們很多的需求都會用到,於是只能自己想辦法實現了。我們可以通過適配器的getItemViewType方法來實現這個功能。

修改後的適配器代碼:RecyclerHeadFootViewAdapter.java

public class RecyclerHeadFootViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    private List<String> datas;
    private LayoutInflater inflater;

    public static final int TYPE_HEADER=1;//header類型
    public static final int TYPE_FOOTER=2;//footer類型
    private View header=null;//頭View
    private View footer=null;//腳View

    public  RecyclerHeadFootViewAdapter(Context context, List<String> datas){
        inflater=LayoutInflater.from(context);
        this.datas=datas;
    }

    //創建每一行的View 用RecyclerView.ViewHolder包裝
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        if(viewType==TYPE_HEADER){
            return new RecyclerView.ViewHolder(header){};
        }else if(viewType==TYPE_FOOTER){
            return new RecyclerView.ViewHolder(footer){};
        }
        View itemView=inflater.inflate(R.layout.recycler_item,null);
        return new MyViewHolder(itemView);
    }

    //給每一行View填充數據
    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position){
        if(getItemViewType(position)==TYPE_HEADER||getItemViewType(position)==TYPE_FOOTER){
            return;
        }
        MyViewHolder myholder= (MyViewHolder) holder;
        myholder.textview.setText(datas.get(getRealPosition(position)));
    }
    
    //如果有頭部 position的位置是從1開始的  所以需要-1
    public int getRealPosition(int position){
        return header==null?position:position-1;
    }

    //數據源的數量
    @Override
    public int getItemCount() {
        if(header == null && footer == null){//沒有head跟foot
            return datas.size();
        }else if(header == null && footer != null){//head為空&&foot不為空
            return datas.size() + 1;
        }else if (header != null && footer == null){//head不為空&&foot為空
            return datas.size() + 1;
        }else {
            return datas.size() + 2;//head不為空&&foot不為空
        }
    }

    @Override
    public int getItemViewType(int position){
        //如果頭佈局不為空&&位置是第一個那就是head類型
        if(header!=null&&position==0){
            return TYPE_HEADER;
        }else if(footer!=null&&position==getItemCount()-1){//如果footer不為空&&最後一個
            return TYPE_FOOTER;
        }
        return super.getItemViewType(position);
    }

    public void setHeader(View header) {
        this.header = header;

        notifyItemInserted(0);//在位置0插入一條數據,然後刷新
    }

    public void setFooter(View footer) {
        this.footer = footer;
        notifyItemInserted(datas.size()-1);//在尾部插入一條數據,然後刷新
    }

    class MyViewHolder extends RecyclerView.ViewHolder{
        private TextView textview;

        public MyViewHolder(View itemView) {
            super(itemView);
            textview= (TextView) itemView.findViewById(R.id.textview);
        }
    }
}
  • getItemCount

有header跟footer的時候需要在源數據長度基礎上進行增加。

  • getItemViewType

通過getItemViewType判斷不同的類型

  • onCreateViewHolder

通過不同的類型創建item的View

  • onBindViewHolder

如果是header跟footer類型是不需要綁定數據的,header跟footer的View一般在actvity中創建,不需要這邊做處理,所以這兩種類型我們就不往下執行,如果有頭佈局,position==0的位置被header占用了,但是我們的數據源也就是集合的下標是從0開始的,所以這裡需要-1。

  • setHeader

設置頭佈局,在第一行插入一條數據,然後刷新。註意這個方法調用後會有插入的動畫,這個動畫可以使用預設的,也可以自己定義

  • setFooter

設置尾部佈局,在尾部插入一條數據,然後刷新。

添加header跟footer的方法終於封裝好了,在activity中只需要兩行代碼就能添加header,跟ListView調用addHeader方法一樣簡單,又可以happy的玩耍了。這裡需要註意的是我們初始化View的時候,inflate方法需要三個參數。

  • resource 資源id
  • root 父View
  • attachToRoot true:返回父View false:返回資源id生成的View
//添加header
View header=LayoutInflater.from(this).inflate(R.layout.recycler_header,recyclerView,false);
adapter.setHeader(header);

//添加footer
View footer=LayoutInflater.from(this).inflate(R.layout.recycler_footer,recyclerView,false);
adapter.setFooter(footer);

recycler_header跟recycler_footer佈局文件我就不貼出來了,就一個TextView,我們直接看效果圖:
RecyclerView添加頭跟尾

item點擊事件&&增加或刪除帶動畫效果

當我們調用RecyclerView的setOnItemClickListener方法的時候,發現居然沒有,用了RecyclerView你要習慣什麼東西都自己封裝。。。

首先我們從adapter開刀,內部寫一個介面,一個實例變數,提供一個公共方法,設置監聽。

private RecyclerViewItemClick recyclerViewItemClick;

public void setRecyclerViewItemClick(RecyclerViewItemClick recyclerViewItemClick) {
    this.recyclerViewItemClick = recyclerViewItemClick;
}

public interface RecyclerViewItemClick{
    /**
     * item點擊
     * @param realPosition 數據源position
     * @param position view position
     */
    void onItemClick(int realPosition,int position);
}

在onBindViewHolder方法中給item監聽點擊事件

if(recyclerViewItemClick!=null) {
    myholder.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            recyclerViewItemClick.onItemClick(getRealPosition(position),position);
        }
    });
}

在activity的onCreate方法中進行監聽,順便設置item增加刪除動畫。我用的是sdk自帶的預設動畫。

adapter.setRecyclerViewItemClick(recyclerViewItemClick);
recyclerView.setItemAnimator(new DefaultItemAnimator());
private RecyclerHeadFootViewAdapter.RecyclerViewItemClick recyclerViewItemClick=new RecyclerHeadFootViewAdapter.RecyclerViewItemClick() {
    @Override
    public void onItemClick(int realPosition, int position) {
        Log.i("ansen","刪除數據:"+realPosition+" view位置:"+position);
        Log.i("ansen","當前位置:"+position+" 更新item數量:"+(adapter.getItemCount()-position-1));

        datas.remove(realPosition);//刪除數據源
        adapter.notifyItemRemoved(position);//item移除動畫
        //更新position至adapter.getItemCount()-1的數據
        adapter.notifyItemRangeChanged(position,adapter.getItemCount()-position-1);
    }
};

源碼下載

RecyclerView


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

-Advertisement-
Play Games
更多相關文章
  • UI層複習筆記 在main文件中,UIApplicationMain函數一共做了三件事 根據第三個參數創建了一個應用程式對象 預設寫nil,即創建的是UIApplication類型的對象,此對象看成是整個應用程式的一個抽象,負責存儲應用程式的狀態。 根據第四個參數創建了一個應用程式代理類對象 所謂代 ...
  • 昨天 提交App Store 的時候被拒了 We discovered one or more bugs in your app when reviewed on iPhone running iOS 10.2 on Wi-Fi connected to an IPv6 network. Speci ...
  • 我們都知道,開發一個app很大程度依賴服務端:服務端提供介面數據,然後我們展示;另外,開發一個app,還需要美工協助切圖。沒了介面,沒了美工,app似乎只能做成單機版或工具類app,真的是這樣的嗎?先來展示下我的個人app,沒有服務端,沒有美工完成的,換言之,我幹了所有人的活: 這個app叫“微言” ...
  • 1.如果可以重置模擬器 首先試試重置模擬器 2.如果不能重置,可以選擇使用如下命令殺死模擬器服務: killall -9 com.apple.CoreSimulator.CoreSimulatorService //殺死模擬器服務 3.使用如下命令刪除模擬器目錄下是所有文件 rm -rf ~/Lib ...
  • 一、使用Raw文件夾下的資料庫文件 在使用GreenDao框架時,資料庫和數據表都是根據生成的框架代碼來自動創建的,從生成的DaoMaster中的OpenHelper類可以看出: 對應的createAllTables函數代碼: 再接著往下看: 從以上的代碼可以看出GreenDao在第一次使用的時候會 ...
  • 今天在看到App Store 上架過程中,蘋果公司反饋的拒絕原因發現了這麼一個問題: Legal - 5.1.5 Your app uses background location services but does not clarify the purpose of its use in the ...
  • 上一篇文章介紹了Dex文件的熱更新流程,本文將會分析Tinker中對資源文件的熱更新流程。 同Dex,資源文件的熱更新同樣包括三個部分:資源補丁生成,資源補丁合成及資源補丁載入。 本系列將從以下三個方面對Tinker進行源碼解析: 轉載請標明本文來源:http://www.cnblogs.com/y ...
  • 之前在博文中為了更好的給大家演示APP的實現效果,本人瞭解學習了幾種給手機錄屏的方法,今天就給大家介紹兩種我個人用的比較舒服的兩種方法: (1)配置adb環境後,使用cmd命令將手機界面操作演示存為視頻文件 (2)使用Google瀏覽器(Google Chrome)提供的擴展程式Vysor將手機界面 ...
一周排行
    -Advertisement-
    Play Games
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...