Custom View Calendar (TableLayout) is extremely slow - java

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. I need a few functions that the CalendarView doesn't have. So I built a CustomView:
calendar.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)
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:
<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)
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. And using RecyclerView will give you more ability to customize date cells (select/deselect, select range, etc.) , support scrolling, swipe, etc.
Or you can use this library, which provide highly customizable 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 CalenderItems everytime, which contain of a LinearLayout and two TextViews. I changed this to just one TextView with an "\n" to make it look like two TextViews. So I could also remove the Layout and just extend TextView Class. So now, it has to render more than 30 TextViews and Layouts less then before, which makes it way faster.

Related

Can I set two Content views in an activity?

I am new to learning android, and I am writing a program to show a point and its position(coordination) on the screen.
I use TextView to show the coordinates, and using CustomView to draw the point out.
Here is my xml.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mainRelativeLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="#+id/panel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="#+id/tv_pointX"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:text="X : "
android:textSize="24sp" />
<TextView
android:id="#+id/tv_pointY"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="1"
android:text="Y : "
android:textSize="24sp" />
</LinearLayout>
<com.example.pointnsendmsgtest3.svPaintCircle
android:id="#+id/TouchView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="0dp">
</com.example.pointnsendmsgtest3.svPaintCircle>
</LinearLayout>
</RelativeLayout>
And my main activity of the code. I am just wondering that how can I display all the views in one screen.
There is only one view can be displayed on the screen, base on the last setContentView command shows in my script to the compiler.
public class MainActivity extends Activity{
private TextView tv_pointx;
private TextView tv_pointy;
private LinearLayout panel_touch;
private svPaintCircle m_view;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
requestWindowFeature(Window.FEATURE_NO_TITLE);
m_view = new svPaintCircle(this);
m_view.setOnTouchListener(new MyListener());
setContentView(m_view);
setContentView(R.layout.activity_main);
}
public class MyListener implements View.OnTouchListener {
public boolean onTouch(View v, MotionEvent event){
testpoint(event);
return true;
}
private void testpoint(MotionEvent event){
if(!debugOn){
return;
}
//initialize mXs and mYs
ArrayList<Float> mXs = null;
ArrayList<Float> mYs = null;
if (mXs == null && mYs == null){
mXs = new ArrayList<Float>();
mYs = new ArrayList<Float>();
}
mXs.clear();
mYs.clear();
final int N = event.getPointerCount();
float x,y;
for(int i = 0; i < N; i++){
x = event.getX(event.getPointerId(i));
y = event.getY(event.getPointerId(i));
logd("x[" + i + "],y[" + i + "] = " + x + "," + y);
mXs.add(x);
mYs.add(y);
}
if(N > 0)m_view.setPoints(mXs,mYs);
}
}
private final boolean debugOn = true;
private final String TAG = "MyListener";
private int logd(String msg) {
int retVal = 0;
if (debugOn) {
retVal = Log.i(TAG, msg);
}
return retVal;
}
}
Here is the class how to draw the point immediately.
public class svPaintCircle extends android.support.v7.widget.AppCompatImageView {
public svPaintCircle(Context context){
super(context);
}
public svPaintCircle(Context context, AttributeSet attrs) {super(context, attrs);}
public svPaintCircle(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}
#Override
protected void onDraw(Canvas canvas){
super.onDraw(canvas);
drawTouchPoint(canvas);
}
ArrayList<Float> mXs = null, mYs = null;
private boolean mDrawn = true;
private Paint mPointPaint = null;
private Paint mRectPaint = null;
private Paint mTextPaint = null;
public void setPoints(ArrayList<Float> mXs, ArrayList<Float> mYs){
if(debugOn) {
if(mPointPaint == null) {
mPointPaint = new Paint();
mPointPaint.setAntiAlias(false);
mPointPaint.setARGB(255,0,96,255);
mRectPaint = new Paint();
mRectPaint.setARGB(0x88,0x44,0x44,0x44); // 0x88 = 136 , 0x44 = 68
mTextPaint = new Paint();
mTextPaint.setTextSize(45);
mTextPaint.setARGB(0xff,0xff,0xff,0xff);
logd("init Paint");
}
this.mXs = mXs;
this.mYs = mYs;
mDrawn = false;
invalidate();
}
}
public void drawTouchPoint(Canvas canvas){
if(debugOn){
if(!mDrawn){
float x,y,rx,ry;
float dx = 80, dy = 80, r = 10;
for(int i = 0; i < mXs.size(); i++){
x = mXs.get(i);
y = mYs.get(i);
//draw cross
//canvas.drawLine(x, y - dy, x, y+dy, mPointPaint);
//canvas.drawLine(x - dx, y, x+dx ,y, mPointPaint);
canvas.drawCircle(x,y,r, mPointPaint);
rx = x;
ry = y - 40;
if(x + 75 > getRight())
rx = x -76;
if(ry < getTop())
ry = y + 20;
canvas.drawRect(0, 0, 320, 45, mRectPaint);
canvas.drawText("x: " + (int)x + " , y:" + (int)y, 0,35, mTextPaint);
}
mDrawn = true;
}
}
}
private final boolean debugOn = true;
private final String TAG = "PointView";
private int logd(String msg){
int retVal = 0;
if(debugOn){
retVal = Log.i(TAG, msg);
}
return retVal;
}
}
How can I make both TextView and CustomView shows together?
no, you can't. setContentView can only be done for one layout.
To add some value to my answer, I would assume your understanding of setContentView is a bit wrong. setContentView is how you tell android which layout file to associate with this activity (as per your example). that's how layouts with different components are done, it's one single file with multiple or different views inside one file (or being referenced from this file, with methods such as include in xml).
From the documentation:
Set the activity content from a layout resource. The resource will be inflated, adding all top-level views to the activity.
https://developer.android.com/reference/android/app/Activity.html#setContentView(int)

How to fix ArrayAdapter changing positions when listView scrolls?

I still not used a ViewHolder class, (I know about the performance of the list view, and will implement it as soon as possible) but I already try overwrite those two methods on array adapter (getViewTypeCount() and getItemViewType) but continue not working.
At my standpoint my array adapter onle handle with ONE type of view, so theoretically I didn't have to overwrite those methods, but i dont know what is happening, just when I scroll my listView the positions get disorganized.
My ArrayAdapter:
private Context context;
private ArrayList<Task> taskArrayList;
public TasksAdapter(#NonNull Context c, #NonNull ArrayList<Task> objects) {
super(c, R.layout.task_row, objects);
this.context = c;
this.taskArrayList = objects;
}
#Override
public int getViewTypeCount() {
return taskArrayList.size();
}
#Override
public int getItemViewType(int position) {
return position;
}
#NonNull
#Override
public View getView(int position, #Nullable View convertView, #NonNull ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(
Context.LAYOUT_INFLATER_SERVICE );
Task task = taskArrayList.get(position);
View view = inflater.inflate(R.layout.task_row, parent, false);
if(TaskActivity.getIsClicked() && TaskActivity.getPositionClicked()-1 == position){
view.setBackgroundResource(R.color.backgroundSelectedItem);
}
TextView timeTextView = view.findViewById(R.id.timeTextView);
TextView taskNameView = view.findViewById(R.id.taskNameText);
TextView dateTextView = view.findViewById(R.id.dateTextView);
FloatingActionButton fab = view.findViewById(R.id.myFabTask);
SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM");
String dateString = sdf.format(task.getDate());
int hour = (int) task.getTime()/3600000;
int minutes = (int) (task.getTime() % 3600000) / 60000;
String stringHour = Integer.toString(hour);
String stringMinutes = Integer.toString(minutes);
if(stringHour.length() == 1){stringHour = "0"+stringHour;}
if(stringMinutes.length() == 1){stringMinutes = "0"+stringMinutes;}
String timeString = stringHour+":"+stringMinutes;
timeTextView.setText(timeString);
taskNameView.setText(task.getName());
dateTextView.setText(dateString);
return view;
}
My Main code where i call and use the adapter:
mTaskListView = findViewById(R.id.task_list_view);
refreshAdapter();
isClicked = false;
positionClicked = 0;
mTaskListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if(positionClicked == (position+1) || positionClicked == 0){
if(!isClicked){
isClicked = true;
positionClicked = position+1;
mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
changeLayoutVisibility(1);
Task task = adapter.getItem(position);
parent.getChildAt(position).setBackgroundColor(getResources().getColor(R.color.backgroundSelectedItem));
}else {
isClicked = false;
positionClicked = 0; mDrawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
changeLayoutVisibility(0);
parent.getChildAt(position).setBackgroundColor(Color.WHITE);
}
}
}
});
Function on Main activity:
private void refreshAdapter() {
adapter = null;
//Get the array with tasks
adapter = new TasksAdapter(this, mDBAdapter.getAllTask());
//Set the list view
mTaskListView.setAdapter(adapter);
}
My row of listView:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#android:color/white"
android:descendantFocusability="blocksDescendants"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="#+id/timeTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="25dp"
android:layout_marginStart="25dp"
android:layout_marginTop="17dp"
android:layout_marginBottom="17dp"
android:text="00:00"
android:textColor="#color/controlColorNormal"
android:textSize="30sp" />
<TextView
android:id="#+id/taskNameText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="#+id/timeTextView"
android:layout_marginLeft="38dp"
android:layout_marginStart="38dp"
android:layout_toEndOf="#+id/timeTextView"
android:layout_toRightOf="#+id/timeTextView"
android:text="Nome da tarefa"
android:textColor="#android:color/black"
android:textSize="16sp" />
<TextView
android:id="#+id/dateTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/taskNameText"
android:layout_alignStart="#+id/taskNameText"
android:layout_below="#+id/taskNameText"
android:text="Data adicionada"
android:textColor="#color/colorHint" />
<android.support.design.widget.FloatingActionButton
android:id="#+id/myFabTask"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="20dp"
android:layout_marginRight="20dp"
android:layout_marginTop="16dp"
android:src="#mipmap/ic_action_send"
app:elevation="4dp"
app:fabSize="mini">
</android.support.design.widget.FloatingActionButton>
My List View:
<ListView
android:id="#+id/task_list_view"
android:layout_width="368dp"
android:layout_height="475dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:defaultFocusHighlightEnabled="true"
android:divider="#color/borderColor"
android:dividerHeight="1px"
android:gravity="center_horizontal|top"
android:theme="#style/MyPicker"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
You said that your adapter only handle with ONE type of view but:
When #Override getViewTypeCount() you return the size of taskArrayList, so the system understand that you have taskArrayList.size() type of view. In your case, it should be 1.
List item In getItemViewType() method, the value return must is the identify of view type, not the item's position. In your case also, it should return 0 as default.
Hope it help!
I just solve it by changing:
parent.
getChildAt(position).
setBackgroundColor(getResources().getColor(R.color.backgroundSelectedItem));
By:
view.setBackgroundResource(R.color.backgroundSelectedItem);
Don't do following operations in getView().
//get and format the date
SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM");
String dateString = sdf.format(task.getDate());
//convert the milliseconds to minute and hour
int hour = (int) task.getTime()/3600000;
int minutes = (int) (task.getTime() % 3600000) / 60000;
String stringHour = Integer.toString(hour);
String stringMinutes = Integer.toString(minutes);
if(stringHour.length() == 1){stringHour = "0"+stringHour;}
if(stringMinutes.length() == 1){stringMinutes = "0"+stringMinutes;}
String timeString = stringHour+":"+stringMinutes;
Create new Function which return type is String and call that function in timeTextView.setText.
Make function Like this:
private String getTimeStirng(Object date){
//get and format the date
SimpleDateFormat sdf = new SimpleDateFormat("EEE, d MMM");
String dateString = sdf.format(date);
//convert the milliseconds to minute and hour
int hour = (int) task.getTime()/3600000;
int minutes = (int) (date % 3600000) / 60000;
String stringHour = Integer.toString(hour);
String stringMinutes = Integer.toString(minutes);
if(stringHour.length() == 1){stringHour = "0"+stringHour;}
if(stringMinutes.length() == 1){stringMinutes = "0"+stringMinutes;}
String timeString = stringHour+":"+stringMinutes;
return timeString;
}
Call it like: timeTextView.setText(getTimeString(task.getDate()));
Thanks.
Happy Coding.

Speeding up big set of programmatically created buttons display

I have an activity which generates a scrollable list (let's say a column) of programmatically created buttons from a List which is the result of an sqlite table read and my problem is that as the List is growing (and so the number of buttons) the initial painting of the screen is becoming slow (at the moment is taking 3 seconds with 50 buttons to draw) so I'm looking for a solution to this.
At first I thought of using a thread (runnable, handler or whatever is best), let's say creating a new thread inside the For which iterates over the list but it's not working (or at least I'm not being able to make it to work) so my question is the next:
Starting from a List<> which is the most appropiate way to create a big set of scrollable buttons so users doesn't have such delay when accesing the screen.
Paginating could be an option, but I'd like to know about other possibilities first and leave that as a last resource.
Thanks, and below is my code.
public static void createButtons(LinearLayout llContainer,
List<TestType> TestTypes, List<Test> Tests,
int buttonFontSize) {
Context oContext = llContainer.getContext();
String strTheme = TMAppearance.getThemeFromPreferences(oContext);
testMe = ((ApplicationConfiguration)oContext.getApplicationContext());
int callerActivity = TestTypes!=null ? 2 : 1;
if (TestTypes!=null || Tests!=null) {
int lCols = strTheme.equals(testMe.theme_vivid) ? 1 : 2;
//int sourceElementIndex = 0;
int originListSize = calculateOriginalListSize(callerActivity, TestTypes, Tests);
int lRows = (int) Math.ceil((double)originListSize/lCols);
List<String> aStartColors = TMUtils_ThemeVivid.generateStartColorArray(lRows, oContext);
List<String> aEndColors = TMUtils_ThemeVivid.generateEndColorArray(lRows, oContext);
for (i = 0; i < lRows; i++) {
LinearLayout outerButtonLayout = generateOuterButtonLayout(oContext);
for (j = 0; j < lCols; j++) {
final Thread r = new Thread() {
public void run() {
LinearLayout innerButtonLayout = generateInnerButtonLayout(oContext);
outerButtonLayout.addView(innerButtonLayout, j);
if (sourceElementIndex<originListSize){
final TMButton oButton = new TMButton(oContext);
if (callerActivity==1) { //testMenu
setTestMenuButtonSettings(oButton, sourceElementIndex, Tests);
} else {
if (callerActivity==2) { //testTypeMenu
setTestTypeMenuButtonSettings(oButton, sourceElementIndex, TestTypes);
}
}
if (strTheme.equals(testMe.theme_vivid)){
oButton.fontSize = buttonFontSize;
oButton.gradientStartColor = aStartColors.get(i);
oButton.gradientEndColor = aEndColors.get(i);
}else{
if (strTheme.equals(testMe.theme_purple)){
oButton.gradientStartColor = testMe.btnStartColor_purple;
oButton.gradientEndColor = testMe.btnEndColor_purple;
}
}
configureButton(oButton, callerActivity);
oButton.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
Context oContext = v.getContext();
TMButton oButton = (TMButton) v;
int callerActivity = Integer.valueOf(v.getTag().toString().split("#")[0]);
String sourceId = String.valueOf(v.getTag().toString().split("#")[1]);
if(event.getAction() == MotionEvent.ACTION_DOWN) { //pressed
setButtonPressed(oButton);
TMSound.playButtonSound(oContext);
} else if (event.getAction() == MotionEvent.ACTION_UP) { //released
setButtonReleased(oButton);
startTargetActivity(callerActivity, sourceId, oContext);
}
return true;
}
});
TMAppearance.doButtonAnimation(oContext, oButton, i);
innerButtonLayout.addView(oButton);
sourceElementIndex++;
}
}
};
r.run();
}
llContainer.addView(outerButtonLayout);
}
}
}
0X0nosugar is correct. A RecycleView will provide better performance, but many beginners have more difficulty implementing it and with only 50 buttons performance shouldn't really be an issue. And although I generally like to abide by the rule 'Use the best available solution' I think it is still appropriate to learn how to implement the ListView. So...
You will need to create a custom adapter:
public class MyListDataAdapter extends ArrayAdapter<MyListData> {
private static final String TAG = "MyListDataAdapter";
public MyListDataAdapter(Context context, ArrayList<MyListData> data) {
super(context, 0, data);
}
#Override
public View getView(int position, View convertView, ViewGroup parent){
final MyListData data = getItem(position);
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.edit_list_item, parent, false);
}
// Add a TextView if you need one
final TextView tvName = (TextView) convertView.findViewById(R.id.tvName);
Button btnEditTicketHolder = (Button) convertView.findViewById(R.id.btnEdit);
btnEditTicketHolder.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
long userId = data.getUserId();
String fName = data.getFirstName();
Intent intent = new Intent(getContext(), EditActivity.class);
intent.putExtra("userId", userId);
intent.putExtra("fName", fName);
getContext().startActivity(intent);
}
});
String name = data.getFirstName();
tvName.setText(name);
return convertView;
}
}
Now you need your MyListData class to hold the data:
public class MyListData {
// Add more as you need
private long userId;
private String firstName;
public MyListData(){
}
public void setFirstName(String name){ this.firstName = name; }
public void setUserId(long id){ this.userId = id; }
public String getFirstName(){ return this.firstName; }
public long getUserId(){ return this.userId; }
}
Your custom ListView layout could look something like:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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"
>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
>
<TextView
android:id="#+id/tvName"
android:text="With Whom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
</LinearLayout>
<LinearLayout
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
>
<Button
android:id="#+id/btnEdit"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:paddingRight="10dp"
android:paddingLeft="10dp"
android:text="Edit"
android:backgroundTint="#color/colorAccent"
android:focusable="false"
/>
</LinearLayout>
</RelativeLayout>
In your Activity (eg. in the onCreate() method) where the ListView you will need to populate the data for the ListView. This should not be done on the UI Thread
ArrayList<MyListData> arrayListData = new ArrayList<MyListData>();
MyListDataAdapter adapter = new MyListDataAdapter(this, arrayListData);
for (MyListData g : result) {
adapter.add(g);
}
mLstMy.setAdapter(adapter);
Also in the activity where the ListView is to be maintained set up some onClick event handlers if you need them:
(I find that one of the small advantages of the ListView is that the onClick event is easier it implement than in the RecycleView)
mMyLst = (ListView) findViewById(lstMy);
mMyLst.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long l) {
MyListData data = (MyListData) parent.getItemAtPosition(position);
selectedName = data.getName();
Intent intent = new Intent(ShowListDataActivity.this, MainActivity.class);
startActivity(intent);
}
});
mMyLst.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
MyListData data = (MyistData) adapterView.getItemAtPosition(i);
selectedName = data.getFirstName();
selectedTicketPosition = i;
// removeValue is my own method for removing an entry from the
// list MyListData and then call adapter.notifyDataSetChanged();
removeValue(i);
return true;
}
});

Updating a certain value and saving it in offline database

I'm developing a tiny app that has a value in a textview set to 150,000 by default. Each month I reset the value back to the default. Every time I spend some of that amount, I open my app and enter the amount I spent and the details of what it was spent on.
What I did was create an offline database to store all the times I spent some of that amount, along with it's id and details. Each time I press the "spend" button, the total amount is reduced by the amount I have entered in the EditText.
I haven't implemented how I'll update the total amount yet, I believe I have to use something called sharedpreference number.
This is the main activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button spendMoney = (Button)this.findViewById(R.id.spendMoney);
Button test = (Button)this.findViewById(R.id.test);
TextView totalTxt = (TextView) this.findViewById(R.id.totalTxt);
final EditText spendAmount = (EditText)this.findViewById(R.id.spendAmount);
// int totalAmount = Integer.parseInt(totalTxt.getText().toString());
final Paid_DB db = new Paid_DB(this);
spendMoney.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
db.addPayment(new Payment(0, (Integer.parseInt(spendAmount.getText().toString())), "Test Details"));
}
});
//totalAmount = totalAmount - Integer.parseInt(spendAmount.getText().toString());
// totalTxt.setText(totalAmount);
test.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(null, DetailsActivity.class);
startActivity(i);
}
});
XML file:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.user.paid.MainActivity">
<TextView
android:id="#+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="13dp"
android:layout_marginStart="13dp"
android:layout_marginTop="53dp"
android:fontFamily="sans-serif"
android:text="Total:"
android:textSize="30sp"
tools:layout_editor_absoluteX="25dp"
tools:layout_editor_absoluteY="36dp" />
<EditText
android:id="#+id/spendAmount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="10"
android:inputType="number"
tools:layout_editor_absoluteX="72dp"
tools:layout_editor_absoluteY="143dp"
android:layout_marginBottom="38dp"
android:layout_above="#+id/spendMoney"
android:layout_centerHorizontal="true" />
<Button
android:id="#+id/spendMoney"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Spend Money"
tools:layout_editor_absoluteX="115dp"
tools:layout_editor_absoluteY="215dp"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
<TextView
android:id="#+id/totalTxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/textView3"
android:layout_alignBottom="#+id/textView3"
android:layout_alignEnd="#+id/spendMoney"
android:layout_alignRight="#+id/spendMoney"
android:fontFamily="sans-serif"
android:text="150,000"
android:textSize="30sp"
tools:layout_editor_absoluteX="25dp"
tools:layout_editor_absoluteY="36dp" />
<Button
android:id="#+id/test"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Test"
tools:layout_editor_absoluteX="134dp"
tools:layout_editor_absoluteY="358dp"
android:layout_marginBottom="90dp"
android:layout_alignParentBottom="true"
android:layout_alignLeft="#+id/spendMoney"
android:layout_alignStart="#+id/spendMoney" />
This is the object I enter into the offline database, containing the id, the amount spent, and details of the payment:
public class Payment {
private int id;
private int amount;
private String details;
public Payment(){}
public Payment(int id, int amount, String details) {
this.id = id;
this.amount = amount;
this.details = details;
}
public Payment(int id, int amount) {
this.id = id;
this.amount = amount;
}
public Payment(int amount, String details) {
this.amount = amount;
this.details = details;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAmount() {
return amount;
}
public void setAmount(int amount) {
this.amount = amount;
}
public String getDetails() {
return details;
}
public void setDetails(String details) {
this.details = details;
} }
This is the offline database:
ublic class Paid_DB extends SQLiteOpenHelper {
// All Static variables
// Database Version
private static final int DATABASE_VERSION = 1;
// Database Name
private static final String DATABASE_NAME = "Payments_DB";
// Contacts table name
private static final String PAYMENT_TABLE = "PaymentTable";
// Contacts Table Columns names
private static final String PAY_ID = "id";
private static final String PAY_AMOUNT = "amount";
private static final String PAY_DETAILS = "details";
Paid_DB(Context context) {
super(context, DATABASE_NAME, null, DATABASE_VERSION);
}
#Override
public void onCreate(SQLiteDatabase db) {
String CREATE_CONTACTS_TABLE = "CREATE TABLE " + PAYMENT_TABLE + "("
+ PAY_ID + " INTEGER PRIMARY KEY," + PAY_AMOUNT + " INTEGER,"
+ PAY_DETAILS + " TEXT" + ")";
db.execSQL(CREATE_CONTACTS_TABLE);
}
#Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
db.execSQL("DROP TABLE IF EXISTS " + PAYMENT_TABLE);
onCreate(db);
}
public void addPayment(Payment payment){
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
values.put(PAY_AMOUNT, payment.getAmount());
values.put(PAY_DETAILS, payment.getDetails()); // Contact Phone Number
// Inserting Row
db.insert(PAYMENT_TABLE, null, values);
db.close();
}
void addListItem(ArrayList<String> listItem) {
SQLiteDatabase db = this.getWritableDatabase();
ContentValues values = new ContentValues();
for (int i = 0; i < listItem.size(); i++) {
Log.e("vlaue inserting==", "" + listItem.get(i));
values.put(PAY_AMOUNT, listItem.get(i));
db.insert(PAYMENT_TABLE, null, values);
}
db.close(); // Closing database connection
}
Cursor getListItem() {
String selectQuery = "SELECT * FROM " + PAYMENT_TABLE;
SQLiteDatabase db = this.getWritableDatabase();
Cursor cursor = db.rawQuery(selectQuery, null);
return cursor;
}}
And finally, this is the details activity, it contains a list view that stores and displays all the payments that have been made:
public class DetailsActivity extends AppCompatActivity {
ArrayList<String> detailsListArrayList;
private ListView lv;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_details);
lv = (ListView) findViewById(R.id.listView);
detailsListArrayList = new ArrayList<>();
detailsListArrayList.add("Item1");
detailsListArrayList.add("Item2");
detailsListArrayList.add("Item3");
detailsListArrayList.add("Item4");
detailsListArrayList.add("Item5");
Paid_DB db = new Paid_DB(this);
db.addListItem(detailsListArrayList);
Cursor cursor = db.getListItem();
Log.e("count", " " + cursor.getCount());
if (cursor != null) {
cursor.moveToNext();
do {
Log.e("value==", "" + cursor.getString(1));
} while (cursor.moveToNext());
}
ArrayAdapter<String> arrayAdapter = new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
detailsListArrayList );
lv.setAdapter(arrayAdapter);
}
}
XML file:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.user.paid.DetailsActivity">
<ListView
android:id="#+id/listView"
android:layout_width="368dp"
android:layout_height="495dp"
tools:layout_editor_absoluteX="8dp"
tools:layout_editor_absoluteY="8dp" />
Every time I click on the "test" button, the app crashes. And every time I try to add a new entry using the "spend" button, the app also crashes. What did I do wrong?
What I can tell from here (without the error log) is:
1) your app crashes when pressing the test-button because you are starting an activity there without passing a contextobject. Try this:
public void onClick(View v) {
Intent i = new Intent(getApplicationContext(), DetailsActivity.class);
startActivity(i);
}
2) The spend button tries to add a new payment entry to your database. But in your database schema , you declare the PAY_ID as a primary key. Later in your addPayment() you are just passing the PAY_AMOUNT and PAY_DETAILS, so you are missing the PAY_ID which seems to lead to a crash.
EDIT: something like this should return all the payments as an ArrayList (may Contain bugs, just wrote it in this editor).
public ArrayList<Payment> getAllPayments() {
ArrayList<Payment> result = new ArrayList<>();
Cursor c = db.query("PAYMENTS",new String[]{"PAY_ID","PAY_AMOUNT","PAY_DETAIL"},null,null,null,null,null); ;
c.moveToFirst();
while(! c.isAfterLast())
{
int s1=c.getInt(0);
int s2=c.getInt(1);
String s3=c.getString(2);
results.add(new Payment(s1,s2,s3));
c.moveToNext();
}
return results;
}
To update your ListView, you should have a closer look a this link, that explains how to work with ListViews and a custom ListView adapter Custom Adapter for List View

Using a class (DatePicker) insider a fragment on Android

I am trying to fetch the value of a Datepicker inside one of my fragments . I've read the documentation about Fragments in the Android Developer Guide.
Here is my Fragment layout :
<ir.smartlab.persindatepicker.PersianDatePicker
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button
android:id="#+id/btnCheckFal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/relativeLayout1"
android:layout_centerHorizontal="true"
android:layout_marginTop="19dp"
android:text="#string/CheckFal" />
</RelativeLayout>
This is my java class for the fragment :
import ir.smartlab.persindatepicker.util.PersianCalendar;
public class HomeFragment extends Fragment {
private View btnCheckFalAction;
private PersianDatePicker persianDatePicker;
public HomeFragment(){}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_home, container, false);
btnCheckFalAction = (Button) rootView.findViewById(R.id.btnCheckFal);
PersianCalendar pCal = persianDatePicker.getDisplayPersianDate();
btnCheckFalAction.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v)
{
Toast.makeText(getActivity(), "Date is : " + pCal , Toast.LENGTH_LONG).show();
}
});
return rootView;
}
}
pCal in above class return error "Cannot refer to a non-final variable pCal inside an inner class ..."
and here is the DatePicker class:
package ir.smartlab.persindatepicker;
import info.androidhive.slidingmenu.R;
import ir.smartlab.persindatepicker.util.PersianCalendar;
import ir.smartlab.persindatepicker.util.PersianCalendarConstants;
import ir.smartlab.persindatepicker.util.PersianCalendarUtils;
import java.util.Date;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.NumberPicker;
public class PersianDatePicker extends LinearLayout {
private NumberPicker yearNumberPicker;
private NumberPicker monthNumberPicker;
private NumberPicker dayNumberPicker;
private int minYear;
private int maxYear;
private int yearRange;
public PersianDatePicker(Context context) {
this(context, null, -1);
}
public PersianDatePicker(Context context, AttributeSet attrs) {
this(context, attrs, -1);
}
public PersianDatePicker(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.sl_persian_date_picker, this);
yearNumberPicker = (NumberPicker) view.findViewById(R.id.yearNumberPicker);
monthNumberPicker = (NumberPicker) view.findViewById(R.id.monthNumberPicker);
dayNumberPicker = (NumberPicker) view.findViewById(R.id.dayNumberPicker);
PersianCalendar pCalendar = new PersianCalendar();
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.PersianDatePicker, 0, 0);
yearRange = a.getInteger(R.styleable.PersianDatePicker_yearRange, 10);
/*
* Initializing yearNumberPicker min and max values If minYear and
* maxYear attributes are not set, use (current year - 10) as min and
* (current year + 10) as max.
*/
minYear = a.getInt(R.styleable.PersianDatePicker_minYear, pCalendar.getPersianYear() - yearRange);
maxYear = a.getInt(R.styleable.PersianDatePicker_maxYear, pCalendar.getPersianYear() + yearRange);
yearNumberPicker.setMinValue(minYear);
yearNumberPicker.setMaxValue(maxYear);
int selectedYear = a.getInt(R.styleable.PersianDatePicker_selectedYear, pCalendar.getPersianYear());
if (selectedYear > maxYear || selectedYear < minYear) {
throw new IllegalArgumentException(String.format("Selected year (%d) must be between minYear(%d) and maxYear(%d)", selectedYear, minYear, maxYear));
}
yearNumberPicker.setValue(selectedYear);
yearNumberPicker.setOnValueChangedListener(dateChangeListener);
/*
* initializng monthNumberPicker
*/
boolean displayMonthNames = a.getBoolean(R.styleable.PersianDatePicker_displayMonthNames, false);
monthNumberPicker.setMinValue(1);
monthNumberPicker.setMaxValue(12);
if (displayMonthNames) {
monthNumberPicker.setDisplayedValues(PersianCalendarConstants.persianMonthNames);
}
int selectedMonth = a.getInteger(R.styleable.PersianDatePicker_selectedMonth, pCalendar.getPersianMonth());
if (selectedMonth < 1 || selectedMonth > 12) {
throw new IllegalArgumentException(String.format("Selected month (%d) must be between 1 and 12", selectedMonth));
}
monthNumberPicker.setValue(selectedMonth);
monthNumberPicker.setOnValueChangedListener(dateChangeListener);
/*
* initializiing dayNumberPicker
*/
dayNumberPicker.setMinValue(1);
dayNumberPicker.setMaxValue(31);
int selectedDay = a.getInteger(R.styleable.PersianDatePicker_selectedDay, pCalendar.getPersianDay());
if (selectedDay > 31 || selectedDay < 1) {
throw new IllegalArgumentException(String.format("Selected day (%d) must be between 1 and 31", selectedDay));
}
if (selectedMonth > 6 && selectedMonth < 12 && selectedDay == 31) {
selectedDay = 30;
} else {
boolean isLeapYear = PersianCalendarUtils.isPersianLeapYear(selectedYear);
if (isLeapYear && selectedDay == 31) {
selectedDay = 30;
} else if (selectedDay > 29) {
selectedDay = 29;
}
}
dayNumberPicker.setValue(selectedDay);
dayNumberPicker.setOnValueChangedListener(dateChangeListener);
a.recycle();
}
NumberPicker.OnValueChangeListener dateChangeListener = new NumberPicker.OnValueChangeListener() {
#Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
int year = yearNumberPicker.getValue();
boolean isLeapYear = PersianCalendarUtils.isPersianLeapYear(year);
int month = monthNumberPicker.getValue();
int day = dayNumberPicker.getValue();
if (month < 7) {
dayNumberPicker.setMinValue(1);
dayNumberPicker.setMaxValue(31);
} else if (month > 6 && month < 12) {
if (day == 31) {
dayNumberPicker.setValue(30);
}
dayNumberPicker.setMinValue(1);
dayNumberPicker.setMaxValue(30);
} else if (month == 12) {
if (isLeapYear) {
if (day == 31) {
dayNumberPicker.setValue(30);
}
dayNumberPicker.setMinValue(1);
dayNumberPicker.setMaxValue(30);
} else {
if (day > 29) {
dayNumberPicker.setValue(29);
}
dayNumberPicker.setMinValue(1);
dayNumberPicker.setMaxValue(29);
}
}
}
};
public Date getDisplayDate() {
PersianCalendar displayPersianDate = new PersianCalendar();
displayPersianDate.setPersianDate(yearNumberPicker.getValue(), monthNumberPicker.getValue(), dayNumberPicker.getValue());
return displayPersianDate.getTime();
}
public void setDisplayDate(Date displayDate) {
setDisplayPersianDate(new PersianCalendar(displayDate.getTime()));
}
public PersianCalendar getDisplayPersianDate() {
PersianCalendar displayPersianDate = new PersianCalendar();
displayPersianDate.setPersianDate(yearNumberPicker.getValue(), monthNumberPicker.getValue(), dayNumberPicker.getValue());
return displayPersianDate;
}
public void setDisplayPersianDate(PersianCalendar displayPersianDate) {
int year = displayPersianDate.getPersianYear();
int month = displayPersianDate.getPersianMonth();
int day = displayPersianDate.getPersianDay();
if (month > 6 && month < 12 && day == 31) {
day = 30;
} else {
boolean isLeapYear = PersianCalendarUtils.isPersianLeapYear(year);
if (isLeapYear && day == 31) {
day = 30;
} else if (day > 29) {
day = 29;
}
}
dayNumberPicker.setValue(day);
minYear = year - yearRange;
maxYear = year + yearRange;
yearNumberPicker.setMinValue(minYear);
yearNumberPicker.setMaxValue(maxYear);
yearNumberPicker.setValue(year);
monthNumberPicker.setValue(month);
dayNumberPicker.setValue(day);
}
#Override
protected Parcelable onSaveInstanceState() {
// begin boilerplate code that allows parent classes to save state
Parcelable superState = super.onSaveInstanceState();
SavedState ss = new SavedState(superState);
// end
ss.datetime = this.getDisplayDate().getTime();
return ss;
}
#Override
protected void onRestoreInstanceState(Parcelable state) {
// begin boilerplate code so parent classes can restore state
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
SavedState ss = (SavedState) state;
super.onRestoreInstanceState(ss.getSuperState());
// end
setDisplayDate(new Date(ss.datetime));
}
static class SavedState extends BaseSavedState {
long datetime;
SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
this.datetime = in.readLong();
}
#Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeLong(this.datetime);
}
// required field that makes Parcelables from a Parcel
public static final Parcelable.Creator<SavedState> CREATOR = new Parcelable.Creator<SavedState>() {
#Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
#Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
}
}
It's simple.
Make pCal final, if you don't have to change its value during execution.
OR
Make pCal a class variable if you don't have any problems with a wider scope.
Declare pCal variable as final and initialise persianDatePicker view using findViewById().
You can change the code like this,
btnCheckFalAction = (Button) rootView.findViewById(R.id.btnCheckFal);
persianDatePicker = (PersianDatePicker) rootView.findViewById(R.id.persianDatePicker);
PersianCalendar pCal = persianDatePicker.getDisplayPersianDate();
and also edit your xml like this,
<ir.smartlab.persindatepicker.PersianDatePicker
android:id="#+id/persianDatePicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>

Categories