简体   繁体   English

自定义视图日历(TableLayout)非常慢

[英]Custom View Calendar (TableLayout) is extremely slow

I have created a Calendar Custom View from scratch.我从头开始创建了一个日历自定义视图。 The reason I don't use the CalendarView is that I want to customize it myself.我不使用CalendarView的原因是我想自己自定义它。 I need a few functions that the CalendarView doesn't have.我需要一些CalendarView没有的功能。 So I built a CustomView:所以我建立了一个CustomView:

calendar.xml:日历.xml:

<merge
        xmlns:android="http://schemas.android.com/apk/res/android"
       xmlns:app="http://schemas.android.com/apk/res-auto"
       android:orientation="vertical"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:background="@android:color/transparent" 
        android:id="@+id/calender">

    <TextView
            android:id="@+id/calender_text_view_month"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="@id/calender_button_next_month"
            app:layout_constraintBottom_toBottomOf="@id/calender_button_next_month"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            android:textColor="@color/colorPrimary"
            android:textSize="20sp"
    />

    <ImageButton
            android:id="@+id/calender_button_next_month"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintHorizontal_bias="0.8"
            app:layout_constraintTop_toTopOf="parent"
            android:src="@drawable/ic_navigate_next_colorprimary_36dp"
            android:background="@android:color/transparent"
            android:contentDescription="@null"/>

    <ImageButton
            android:id="@+id/calender_button_prev_month"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintHorizontal_bias="0.2"
            android:src="@drawable/ic_navigate_before_colorprimary_36dp"
            android:background="@android:color/transparent"
            android:contentDescription="@null"
    />
    <TableLayout
            android:id="@+id/calender_table_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@id/calender_text_view_month"
            android:layout_marginTop="30dp"
            android:divider="@drawable/recyclerview_divider"
            android:showDividers="middle"
            android:background="@drawable/table_stroke_color_primary"
    />

</merge>

Calendar.java: (Here I have deleted a few methods that are not important, like getDaysOfMonth . This is just a little bit of math) Calendar.java:(这里我删除了一些不重要的方法,比如getDaysOfMonth 。这只是一点点数学)

public class Calender extends ConstraintLayout {

    private TextView tvMonth;
    private TableLayout calender;

    private int year;
    private int month;
    private Calendar c;

    private int selectedDay = -1;
    private int selectedMonth = -1;
    private int selectedYear = -1;

    private Context context;

    public Calender(Context context) {
        this(context, null);
    }

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

    public Calender(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        this.context = context;

        LayoutInflater l = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        l.inflate(R.layout.calender, this, true);

        tvMonth = findViewById(R.id.calender_text_view_month);
        calender = findViewById(R.id.calender_table_layout);

        c = Calendar.getInstance();
        month = c.get(Calendar.MONTH);
        year = c.get(Calendar.YEAR);
        makeCalender();
    }

    private void makeCalender() {
        ArrayList<TableRow> rows = new ArrayList<>();
        tvMonth.setText(String.format("%s %s", month, year));
        calender.removeAllViews();
        calender.addView(createCalenderHeader());
        int dayOfWeek = getFirstDayOfCurrentMonth();
        int daysOfMonth = getDaysOfMonth(month, year);
        int daysPrevMonth = month > 0 ? getDaysOfMonth(month - 1, year) : getDaysOfMonth(11, year - 1);
        int prevMonth = month > 0 ? month - 1 : 11;
        int prevYear = month > 0 ? year : year - 1;
        TableRow tr1 = new TableRow(context);
        tr1.setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT));
        tr1.setShowDividers(TableRow.SHOW_DIVIDER_MIDDLE);
        tr1.setDividerDrawable(ContextCompat.getDrawable(context, R.drawable.table_row_divider));
        for (int day = 1; day <= dayOfWeek; day++) {
            tr1.addView(createCalenderItemPrev(daysPrevMonth + day - dayOfWeek, prevMonth, prevYear));
        }

        for (int day = 1; day <= daysOfMonth; day++) {
            if((dayOfWeek + day) <= 7) {
                tr1.addView(createCalenderItem(day, month, year));
                if(dayOfWeek + day == 7)
                    calender.addView(tr1);
                continue;
            }
            if (rows.size() == 0 ||rows.get(rows.size() - 1).getChildCount() == 7) {
                rows.add(new TableRow(context));

                rows.get(rows.size() - 1).setShowDividers(TableRow.SHOW_DIVIDER_MIDDLE);
                rows.get(rows.size() - 1).setDividerDrawable(ContextCompat.getDrawable(context, R.drawable.table_row_divider));
                rows.get(rows.size() - 1).setLayoutParams(new TableRow.LayoutParams(TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.WRAP_CONTENT));
            }
            rows.get(rows.size() - 1).addView(createCalenderItem(day, month, year));
        }
        c.set(year, month, daysOfMonth);
        int lastDayOfMonth = c.get(Calendar.DAY_OF_WEEK);
        if(lastDayOfMonth != 1) {
            int nextMonth = month < 11 ? month + 1 : 0;
            int nextYear = month < 11 ? year : year + 1;
            for (int i = lastDayOfMonth; i <= 7; i++) {
                rows.get(rows.size() - 1).addView(createCalenderItemPrev(i - lastDayOfMonth + 1, nextMonth, nextYear));
            }
        }

        for(int i = 0; i < rows.size(); i++) {
            calender.addView(rows.get(i));
        }
    }

    private CalenderItem createCalenderItem(int day, int month, int year) {
        CalenderItem item = new CalenderItem(context);
        item.setDate(day, month, year);
        item.setOnItemClickedListener(new CalenderItem.IItemClicked() {
            @Override
            public void itemClicked(int day, int month, int year) {
                selectedDay = day;
                selectedMonth = month;
                selectedYear = year;
                makeCalender();
            }
        });
        return item;
    }

    private CalenderItem createCalenderItemPrev(int day, int month, int year) {
        CalenderItem item = new CalenderItem(context);
        item.setDate(day, month, year);

        return item;
    }
}

calendar_item.xml: calendar_item.xml:

<merge
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:clickable="true"
        android:focusable="true"
>

    <TextView
            android:id="@+id/text_view_calender_item_date"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toTopOf="parent"
            android:layout_marginTop="4dp"
            android:clickable="false"
            android:textAlignment="center"
    />
    <TextView
            android:id="@+id/text_view_calender_item_routine"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintTop_toBottomOf="@id/text_view_calender_item_date"
            android:layout_marginBottom="4dp"
            app:layout_constraintBottom_toBottomOf="parent"
            android:clickable="false"
            android:textAlignment="center"
    />

</merge>

CalendarItem.java: (Again, I deleted the parts that are not important) CalendarItem.java:(再次删除不重要的部分)

public class CalenderItem extends ConstraintLayout {

    private final TextView tvDate;
    private final TextView tvRoutine;

    private IItemClicked listener = null;
    public void setOnItemClickedListener(IItemClicked listener) {
        this.listener = listener;
    }

    public CalenderItem(Context context) {
        this(context, null);
    }

    public CalenderItem(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public CalenderItem(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);

        LayoutInflater l = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        l.inflate(R.layout.calender_item, this, true);
        tvDate = findViewById(R.id.text_view_calender_item_date);
        tvRoutine = findViewById(R.id.text_view_calender_item_routine);
        setLayoutParams(new TableRow.LayoutParams(0, TableRow.LayoutParams.WRAP_CONTENT, 1));
        setBackgroundResource(R.drawable.background_calender_item);
        setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if(listener != null) {
                    listener.itemClicked(day, month, year);
                }
            }
        });
    }

    public interface IItemClicked {
        void itemClicked(int day, int month, int year);
    }
}

This Calendar Custom View works.此日历自定义视图有效。 Every Item is where it should be.每个项目都在它应该在的地方。 But the thing is that it takes extremely long to build, up to a second or two.但问题是构建需要非常长的时间,最多一两秒。 Everytime I open the fragment with the calendar, the app freezes for one or two seconds.每次我用日历打开片段时,应用程序都会冻结一两秒钟。 I can't explain why this is.我无法解释为什么会这样。

I can edit the implementation or some other code if you want.如果需要,我可以编辑实现或其他代码。 But I think that is everything what's important.但我认为这才是最重要的。

You can improve your custom CalendarView by using RecyclerView instead of TableView .您可以使用RecyclerView而不是TableView来改进您的自定义CalendarView And using RecyclerView will give you more ability to customize date cells (select/deselect, select range, etc.), support scrolling, swipe, etc.并且使用RecyclerView会给你更多的自定义日期单元格的能力(选择/取消选择、select 范围等),支持滚动、滑动等。

Or you can use this library , which provide highly customizable CalendarView或者你可以使用这个库,它提供了高度可定制的CalendarView

Okay, I will answer the question myself, because I managed to solve the issue.好的,我会自己回答这个问题,因为我设法解决了这个问题。 It is so slow, because is is rendering more then 30 CalenderItem s everytime, which contain of a LinearLayout and two TextView s.它是如此缓慢,因为每次渲染超过 30 个CalenderItem ,其中包含一个LinearLayout和两个TextView I changed this to just one TextView with an "\n" to make it look like two TextView s.我将其更改为只有一个带有“\n”的TextView ,使其看起来像两个TextView So I could also remove the Layout and just extend TextView Class.所以我也可以删除布局并扩展TextView Class。 So now, it has to render more than 30 TextView s and Layouts less then before, which makes it way faster.所以现在,它必须渲染超过 30 个TextView和比以前更少的布局,这使得它更快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM