自定義布局---TimeTableLayout(課程表布局)

一直在做一款教務系統(tǒng)的移動端應用,先前做的課程表控件不太滿意,最近又在布局和功能上稍作調整。實現(xiàn)方式很普通,可以說是low,但效果如期就好,下面就開始給大家介紹此布局如何實現(xiàn)。

1.還是先看效果:

在電腦上顯示效果不太好,部署到真機上,還是蠻不錯的。


演示.gif

真機錄完轉GIF不清楚,來看大圖:

a.png

b.png

c.png

此自定義布局直接繼承自RelativeLayout,思路就是動態(tài)計算尺寸,add布局。缺點:因為是業(yè)務需要,所以就沒有考慮擴展性,比如:表格里的View自定義之類的。但作為一個課程表控件,我覺得他應該滿足了大部分需求。

2.分析

該組合布局總體分為4個部分:
(1).左上角單獨的一個TextView,作為布局的標桿,后續(xù)的布局以其id設置位置。
(2).上方右側部分的星期數(shù),采用的是LinearLayout 包裹上下兩個TextView,根據(jù)id,動態(tài)設置其位置。
(3).下方的節(jié)次和課程繪制區(qū)域,整體使用ScrollView包裹,使用LinearLayout劃分左右區(qū)域。左邊顯示節(jié)次(垂直LinearLayout),右邊是課程繪制區(qū)域(Framelayout)
(4).繪制浮在最上層的課程格子(FrameLayout+TextView)

描述的不是很清楚,那么上一張圖吧^ ^

無標題.png

3.實現(xiàn)

在寫之前,還有必要提一個非常重要的角色,就是本例使用的數(shù)據(jù)結構(javaBean),結構如下:

public class Course {
  /**
   * 課程開始的節(jié)次 
   */
  private int jieci;
  private int day;
  private String des;
  private int spanNum = 2;// 默認跨越兩節(jié)
  public Course(int jieci, int day, String des) {
    this.jieci = jieci;
    this.day = day;
    this.des = des;
  }
  public Course() {
  }
  //省略Get,Set方法...
}

既然摸清了這個布局的來龍去脈,那么就可以按照分析中的步驟,一步步實現(xiàn)這個自定義課程表布局。

3.1 創(chuàng)建View,在構造方法中定義Init方法。
public class TimeTableLayout extends RelativeLayout {
    
    //今天周幾(中國的周日=0,周一=1..... 周六=6)此處是減了1的,方便數(shù)組中對應上
    private int todayNum;
    //和中國星期數(shù)對應上
    private int[] US_DAYS_NUMS = { 7, 1, 2, 3, 4, 5, 6 };
    //星期數(shù)對應在這個月是幾號(具體看下面介紹)
    private String[] datesOfMonth;

    public TimeTableLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //對相關變量進行初始化以及繪制的課程表布局框架
        init();
    }

    public TimeTableLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TimeTableLayout(Context context) {
        this(context, null);
    }
    private void init() {
        Calendar toDayCal = Calendar.getInstance();
        //設置為今天
        toDayCal.setTimeInMillis(System.currentTimeMillis());
        //得到今天是周幾,注意此處的周幾是美歷的,不是中國的。
        //toDayCal.get(Calendar.DAY_OF_WEEK)返回(1~7)之中的數(shù)
        //中國的周日=1,周一=2..... 周六=7
        todayNum = toDayCal.get(Calendar.DAY_OF_WEEK)-1;
        //得到這一周所對應的日期(day of month)
        datesOfMonth = getOneWeekDatesOfMonth();
        //繪制整個課程表布局框架
        drawFrame();
    }        
}

在init方法中,我們先拿到今天是周幾(美歷),然后需要動態(tài)的根據(jù)今天計算出這周的其他日期,我們看先看getOneWeekDatesOfMonth():

//默認共有幾天
private int totalDay = 7;
//左上角的TextView顯示的月份,即周一所對應的月份
private String preMonth;
/**
 * 獲取以今天為基準 ,星期一到星期日在這個月中是幾號
 * @return 
 */
private String[] getOneWeekDatesOfMonth() {
    Calendar tempCal= Calendar.getInstance();
    //存儲日期
    String[] temp = new String[totalDay];
    //獲得中國的周幾
    int b = US_DAYS_NUMS[todayNum];
    //如果今天不是周日,也就是說美歷的下周還沒開始,則直接設置為本周周一
    if (b != 7) {
        tempCal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
    } else {
        //如果是周日的話,已經(jīng)是美歷的下周的周一了,所以上先跳到上周。
        tempCal.add(Calendar.WEEK_OF_MONTH, -1);
        //跳到上周后再設置為周一
        tempCal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
    }
    int ds = 0;//此臨時變量記錄周一為幾號
    for (int i = 1; i < totalDay; i++) {
        if (i == 1) {//如果為周一
            ds = tempCal.get(Calendar.DAY_OF_MONTH);
            //設置周一為幾號
            temp[i - 1] = tempCal.get(Calendar.DAY_OF_MONTH) + "";
            //記錄一下周一所對應的月份
            preMonth = (tempCal.get(Calendar.MONTH) + 1) + "月";
        }
        //往后加一天
        tempCal.add(Calendar.DATE, 1);
        //如果這天比先前記錄的日期號小,說明進入到了下一個月份
        if (tempCal.get(Calendar.DAY_OF_MONTH) < ds) {
            //則不顯示日期號,顯示這天的月份    
            temp[i] = (tempCal.get(Calendar.MONTH) + 1) + "月";
            //重新對ds賦值
            ds = tempCal.get(Calendar.DAY_OF_MONTH);
        } else {
            //其他情況均顯示這天所對應的日期數(shù)
            temp[i] = tempCal.get(Calendar.DAY_OF_MONTH) + "";
        }
    }
    //將結果數(shù)組返回,可能的格式:{"30","31","9月","2","3","4","5"}
    return temp;
}

好的,下一個方法drawFrame(),繪制布局框架:

private void drawFrame() {
    //初始化格子寬高大小
    initSize();
    // 繪制第一行
    drawFirstRow();
    // 繪制下面的東西,整個下面是一個ScrollView包裹一個LinearLayout
    addBottomRestView();
}

看初始化格子大小的方法,此處還需要引入幾個成員變量:

如圖:

尺寸分析.png
//原諒我當時命名有點啰嗦(現(xiàn)在懶得改了)
//第一行的高度
private int firstRowHeight;
//非第一行 每一行的高度
private int notFirstEveryRowHeight;
//第一列的寬度
private int firstColumnWidth;
//非第一列 每一列的寬度
private int notFirstEveryColumnsWidth;

private void initSize() {
    int screenWidth = getScreenWidth();
    int screenHeight = getScreenHeight();
    //第一行高度為40dp,這個dp->px工具方法在上一篇有用到
    firstRowHeight = DensityUtils.dip2px(getContext(), 40);
    //此處解一個方程,設第一行非第一列格子寬度為x,最左邊的格子為x/2,則totalDay*x+x/2 = screenHeight ; 
    //x=notFirstEveryColumnsWidth ;
    notFirstEveryColumnsWidth = screenWidth * 2 / (2 * totalDay + 1);
    //第一列的寬度為x的一半
    firstColumnWidth = notFirstEveryColumnsWidth / 2;
    //非第一行,每一行的高度為屏幕的高度除以總節(jié)次+5dp
    notFirstEveryRowHeight = (screenHeight - firstRowHeight) / totalJC + DensityUtils.dip2px(getContext(), 5);
}

private int getScreenWidth() {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    wm.getDefaultDisplay().getMetrics(displayMetrics);
    return displayMetrics.widthPixels;
}

private int getScreenHeight() {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    wm.getDefaultDisplay().getMetrics(displayMetrics);
    return displayMetrics.heightPixels;
}

ok,變量都初始化了大小,我們就可以正式繪制我們的課程表啦?。“床襟E,首先繪制第一行drawFirstRow():

/**
 * 繪制第一行
 */
private void drawFirstRow() {
    //繪制左上角的TextView
    initFirstTv();
    //繪制余下的內容,實際上并不是TextView,是LinearLayout包裹的
    initRestTv();
}

/**
 * 起始的第一個TextView
 */
private TextView firstTv;
//2dp
private int twoW = DensityUtils.dip2px(getContext(), 2);
//1dp
private int oneW = DensityUtils.dip2px(getContext(), 1);

private static final int FIRST_TV = 555;

private void initFirstTv() {
    firstTv = new TextView(getContext());
    //設置一個Id,和布局文件里的Id一個意思
    firstTv.setId(FIRST_TV);
    //設置布局參數(shù),其實就是設置寬高,我們在剛剛都算出來了
    RelativeLayout.LayoutParams rlp = new RelativeLayout.LayoutParams(firstColumnWidth, firstRowHeight);
    firstTv.setBackgroundResource(R.drawable.course_table_bg);
    firstTv.setText(preMonth);
    firstTv.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
    firstTv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
    firstTv.setPadding(oneW, twoW, oneW, twoW);
    firstTv.setLayoutParams(rlp);
    addView(firstTv);
}

第一個TextView繪制完畢,這里順便給大家背景邊框的資源文件,放置在drawable目錄中:

<?xml version="1.0" encoding="utf-8"?>
<layer-list  xmlns:android="http://schemas.android.com/apk/res/android" >
<!-- 邊框的顏色 -->
<item android:right="0dp">
    <shape android:shape="rectangle" >
        <solid android:color="#64bfe6" />
    </shape>
</item>
<!-- 格子的背景顏色 -->
<item
    android:bottom="0.5dp"
    android:right="0.5dp">
    <shape android:shape="rectangle" >
        <solid android:color="#eed9ecfd" />
    </shape>
</item>
</layer-list>

那么接著繪制剩下的View,方法有些長,沒有抽出,很多都是參數(shù)設置,關鍵代碼就幾句:

private static final int FIRST_ROW_TV_QZ = 3;

private void initRestTv() {
    LinearLayout linearLayout;
    RelativeLayout.LayoutParams rlp;
    TextView textView
    for (int i = 0; i < totalDay; i++) {
        //這使用LinearLayout(垂直)包裹兩個TextView
        linearLayout = new LinearLayout(getContext());
        linearLayout.setOrientation(LinearLayout.VERTICAL);
        //設置一個Id,加上前綴以防止重復(突然發(fā)現(xiàn)不加也行)
        linearLayout.setId(FIRST_ROW_TV_QZ + i);
        //設置寬高
        rlp = new RelativeLayout.LayoutParams(notFirstEveryColumnsWidth,
                firstRowHeight);
        //如果是第一個,則在左上角的TextView右側
        if (i == 0)
            rlp.addRule(RelativeLayout.RIGHT_OF, firstTv.getId());
        //剩余的則后一個在前一個右側
        else
            rlp.addRule(RelativeLayout.RIGHT_OF, FIRST_ROW_TV_QZ + i - 1);
        linearLayout.setBackgroundResource(R.drawable.course_table_bg);
        linearLayout.setLayoutParams(rlp);
        LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT);
        //上方的顯示日期的TextView
        textView = new TextView(getContext());
        textView.setText(datesOfMonth[i]);
        textView.setLayoutParams(llp);
        textView.setGravity(Gravity.CENTER);
        textView.setPadding(twoW, twoW, twoW, twoW);
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
        linearLayout.addView(textView);
        llp = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        //下方的顯示星期數(shù)的TextView
        textView = new TextView(getContext());
        textView.setLayoutParams(llp);
        textView.setText(DAYS[i]);
        textView.setGravity(Gravity.CENTER | Gravity.BOTTOM);
        //此處在今天這個格子中做高亮處理
        if (US_DAYS_NUMS[todayNum] - 1 == i) {
            linearLayout.setBackgroundColor(0x77069ee9);
        }
        textView.setPadding(twoW, 0, twoW, twoW * 2);
        textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 12);
        linearLayout.addView(textView);
        addView(linearLayout);
    }
}

添加完整個上方的區(qū)域后,接著添加下方的區(qū)域,為了可以使布局滾動,我們在最外層使用到了ScrollView作為跟布局,其再包裹一個LinearLayout的水平布局:

//課程格子View的父布局
private FrameLayout flCourseContent;

private void addBottomRestView() {
    ScrollView sv = new ScrollView(getContext());
    LayoutParams rlp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    //其位置在左上角的TextView下面
    rlp.addRule(RelativeLayout.BELOW, firstTv.getId());
    sv.setLayoutParams(rlp);
    //隱藏滾動條
    sv.setVerticalScrollBarEnabled(false);

    //包裹的LinearLayout(默認水平)
    LinearLayout llBottom = new LinearLayout(getContext());
    ViewGroup.LayoutParams vlp = new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    llBottom.setLayoutParams(vlp);

    //左側使用LinearLayout(垂直),包裹節(jié)次的TextView
    LinearLayout llLeftCol = new LinearLayout(getContext());
    LinearLayout.LayoutParams llp1 = new LinearLayout.LayoutParams(firstColumnWidth, LayoutParams.WRAP_CONTENT);
    llLeftCol.setLayoutParams(llp1);
    llLeftCol.setOrientation(LinearLayout.VERTICAL);
    
    //初始化左側顯示節(jié)次的TextView
    initLeftTextViews(llLeftCol);
    llBottom.addView(llLeftCol);

    flCourseContent = new FrameLayout(getContext());
    LinearLayout.LayoutParams llp2 = new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT,
            LayoutParams.MATCH_PARENT);
    flCourseContent.setLayoutParams(llp2);
    //這句是先添加課程格子邊框
    drawCourseFrame();
    llBottom.addView(flCourseContent);

    sv.addView(llBottom);

    addView(sv);
}

接下來便是添加左側的節(jié)次TextView,其實也很簡單,一個循環(huán)搞定,看initLeftTextViews(llLeftCol):

//默認節(jié)次最大12
private int totalJC = 12;

private void initLeftTextViews(LinearLayout llLeftCol) {
    LinearLayout.LayoutParams rlp = new LinearLayout.LayoutParams(firstColumnWidth, notFirstEveryRowHeight);
    TextView textView;
    for (int i = 0; i < totalJC; i++) {
        textView = new TextView(getContext());
        textView.setLayoutParams(rlp);
        textView.setBackgroundResource(R.drawable.course_table_bg);
        //顯示節(jié)次
        textView.setText("" + (i + 1));
        textView.setGravity(Gravity.CENTER);
        textView.setTextColor(Color.GRAY);
        llLeftCol.addView(textView);
    }
}

然后便是右側的FrameLayout和添加課程格子的邊框,其實也很簡單:

private void drawCourseFrame() {
    FrameLayout fl;
    FrameLayout.LayoutParams flp;
    for (int i = 0; i < totalDay * totalJC; i++) {
        int row = i / totalDay;
        int col = i % totalDay;
        fl = new FrameLayout(getContext());
        //設置格子的大小
        flp = new FrameLayout.LayoutParams(notFirstEveryColumnsWidth,
                notFirstEveryRowHeight);
        fl.setBackgroundResource(R.drawable.course_table_bg);
        //這里采用設置Margin值來確定每個格子的背景的位置
        //col(列數(shù)) * 列寬為格子左側偏移量
        //row(行數(shù)) * 行高為格子上方偏移量
        //這樣就可以確定格子的位置(后面添加課程信息 也用的此種方式)
        flp.setMargins(col * notFirstEveryColumnsWidth, row * notFirstEveryRowHeight, 0, 0);
        fl.setLayoutParams(flp);
        flCourseContent.addView(fl);
    }
}

以上,課程表布局的框架就完成了,下面所要做的就是接收數(shù)據(jù),顯示課程信息View。

3.2對外提供接口,添加課程信息

我們給外部提供的方法名稱叫做updateTimeTable(),看代碼:

//用來保存課程信息
private List<? extends Course> coursesData;

//帶參數(shù)的更新課程信息的方法
public void updateTimeTable(List<? extends Course> coursesData) {
    this.coursesData = coursesData;
    updateCourseViews();
}
//不帶參數(shù)的更新方法,需保證持有的List引用和外部一致
public void updateTimeTable() {
    updateCourseViews();
}

這個updateCourseViews方法就是真正來添加課程信息的(方法有些長,但多數(shù)是參數(shù)設置):

// 課程格子的背景圖(下方有示例背景xml)
private static final int[] COURSE_BG = { R.drawable.course_info_light_blue, R.drawable.course_info_green,
        R.drawable.course_info_red, R.drawable.course_info_blue, R.drawable.course_info_yellow,
        R.drawable.course_info_orange, R.drawable.course_info_purple };

private OnCourseItemClickListener onCourseItemClickListener;

public void setOnCourseItemClickListener(OnCourseItemClickListener onCourseItemClickListener) {
    this.onCourseItemClickListener = onCourseItemClickListener;
}
//點擊課程信息的監(jiān)聽事件
public interface OnCourseItemClickListener {
    void onCourseItemClick(TextView tv, int jieci, int day, String des);
}

/**
 * 保存View 方便Remove
 */
private List<View> myCacheViews = new ArrayList<View>();

private void updateCourseViews() {
    //在每次做更新操作時,先清除一下當前的已經(jīng)添加上去的View
    clearViewsIfNeeded();
    FrameLayout fl;
    FrameLayout.LayoutParams flp;
    TextView tv;
    for (final Course c : coursesData) {
        //拿到節(jié)次(相當于行)
        final int jieci = c.getJieci();
        //拿到星期(相當于列)
        final int day = c.getDay();
        //外層包裹一個FrameLayout 方便為TextView設置padding,保證課程信息與邊框有一定距離(2dp)
        fl = new FrameLayout(getContext());
        //設置課程信息的寬高,寬度就是列寬,高度是行高 * 跨度
        flp = new FrameLayout.LayoutParams(notFirstEveryColumnsWidth,
                notFirstEveryRowHeight * c.getSpanNum());
        //設置橫向和縱向的偏移量,和上面介紹的一致,但day和jieci都是從1開始的,需減1.
        flp.setMargins((day - 1) * notFirstEveryColumnsWidth, (jieci - 1) * notFirstEveryRowHeight, 0, 0);
        fl.setLayoutParams(flp);
        fl.setPadding(twoW, twoW, twoW, twoW);

        tv = new TextView(getContext());
        flp = new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        tv.setText(c.getDes());
        tv.setTextColor(Color.WHITE);
        tv.setGravity(Gravity.TOP | Gravity.CENTER_HORIZONTAL);
        tv.setPadding(twoW, twoW, twoW, twoW);
        tv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 10);
        //顯示不下的話,尾部以"..."顯示     
        tv.setEllipsize(TruncateAt.END);
        //設置最大顯示7行
        tv.setLines(7);
        tv.setBackgroundResource(COURSE_BG[day - 1]);
        tv.setLayoutParams(flp);
        tv.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                //為課程信息設置點擊事件監(jiān)聽
                if (onCourseItemClickListener != null)
                    onCourseItemClickListener.onCourseItemClick((TextView) v, jieci, day, c.getDes());
            }
        });
        fl.addView(tv);
        //對每個添加到布局中的課程信息View做一個保存,方便下次清除
        myCacheViews.add(fl);
        flCourseContent.addView(fl);
    }
}

private void clearViewsIfNeeded() {
    if (myCacheViews == null || myCacheViews.isEmpty())
        return;

    for (int i = myCacheViews.size() - 1; i >= 0; i--) {
        flCourseContent.removeView(myCacheViews.get(i));
        myCacheViews.remove(i);
    }
}

以下是課程信息背景的xml代碼,定義7個不同顏色的背景資源即可:

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

     <!--背景顏色-->
    <solid android:color="@color/exam_info_blue_light" />
     <!-- 圓角的半徑 -->
    <corners android:radius="5dp" />

</shape>

至此為止,我們的TimeTableLayout就已經(jīng)可以出色的完成一個課程表該具有的職責了~我后面還加了動態(tài)變換節(jié)次和星期,感覺用處不大,一并貼出來:

public TimeTableLayout(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    //自定義屬性的模板代碼,須在value目錄下建立名為attr的xml文件
    final TypedArray ta = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CourseTable, defStyleAttr,
            0);
    totalDay = ta.getInt(R.styleable.CourseTable_totalDays, 7);
    totalJC = ta.getInt(R.styleable.CourseTable_totalJC, 12);
    ta.recycle();
    init();
}

看value目錄下的attr.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
  <declare-styleable name="CourseTable">
          <attr name="totalDays" format="integer" />
          <attr name="totalJC" format="integer" />
  </declare-styleable>
</resources>

在布局文件中就可以這么用:

<?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="match_parent"
    android:background="@color/white"
    android:orientation="vertical" >

    <include layout="@layout/main_title_layout" />

    <com.tpwy.widget.TimeTableLayout
        android:id="@+id/ctv_course_info"
        android:layout_width="match_parent"
        app:totalDays="7"
        app:totalJC="12"
        android:layout_height="match_parent" >
    </com.tpwy.widget.TimeTableLayout>

</LinearLayout>

在代碼中設置更改節(jié)次和星期數(shù),提供了相應的set方法:

public void setTotalJC(int totalJC) {
    this.totalJC = totalJC;
    refreshCurrentLayout();
}
public void setTotalDay(int totalDay) {
    this.totalDay = totalDay;
    refreshCurrentLayout();
}
private void refreshCurrentLayout() {
    removeAllViews();
    init();
    drawFrame();
    updateCourseViews();
}

4.總結一下

這個布局其實技術上沒有什么難度,就是把布局文件中的代碼移到了java文件中,從靜態(tài)到動態(tài),需要事先把所有尺寸定義計算好,然后通過addView不斷組合添加,最終實現(xiàn)我們想要的效果。這個布局應用的范圍很窄,可擴展性也不高,目的是讓大家不光只會在布局文件中寫布局,使用java代碼一樣能寫出精準的布局來。

這幾天媳婦來了,陪她玩了幾天,沒有學習好難受,這幾天要把攢的干貨一一消滅掉!
Git地址(里面有demo): https://github.com/chen2174471/TimeTableLayout

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 178,769評論 25 709
  • ¥開啟¥ 【iAPP實現(xiàn)進入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 7,295評論 0 17
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,052評論 4 61
  • 第6天 我在不經(jīng)意間卷入了自媒體,是跟小江老師學了后才曉得,我們玩的是自媒體,因為要裝媒,所以我裝得很苦逼。學到第...
    財稅春秋閱讀 461評論 0 0
  • 一 家阿薩德法師打發(fā) 1 克辣椒是地方 2 家卡上的減肥了看 閃亮的會計法 text這里是引用 的規(guī)范化的覆蓋
    KrisWang77閱讀 442評論 0 0

友情鏈接更多精彩內容