Announcing the release of Time Since 1.2
What Changed
- Android Pie Support
- Bug fixes and optimizations
Moveableapps blog
Announcing the release of Time Since 1.2
Announcing the release of Time Since 1.1
Ever wondered how long its been since you took a vacation, started gym, or anything important or mundane took place? You don’t have you scratch your head anymore to remember all these dates/events. Introducing TimeSince for Android, a quick, easy and efficient way to track all things personal and mundane.
Quickly add events by giving it a name, Started gym, a label, Personal, and date, 1st July, 2017. That’s it and TimeSince will track this event for you.
Some for the mundane things in daily routine can be boring to take care of, like your phone bill, credit card bill, or laundry. TimeSince allows you to track repeating events. Whenever you paid your bills or did your laundry just tell TimeSince with just one click and it will start recounting that event again.
Paid your last installment of your car or cut the cord and don’t have to pay your monthly cable bill, with touch of a button TimeSince will stop tracking it. You can still see those past events.
Starting today you can download the free app from Google Play click the link or scan the QR code.
Announcing the release of Expense Tracker 4.0.4
Announcing the release of Expense Tracker 4.0.3
Announcing the release of Expense Tracker 4.0.
Announcing the release of Expense Tracker 3.0.2.
After the update export all your transactions and import them.
public class CustomItemView extends View
onMeasure, onDraw, onLayout, onTouchEvent
. When onMeasure
onLayout
weonDraw
, we draw the view and onTouchEvent
we determine how to respond.onMeasure
method is called to get the measurements for this view, so this method gives us the chance to calculate the dimensions needed. Measure mode MeasureSpec.EXACTLY
or MeasureSpec.AT_MOST
gives us hints on how much width or height is available to work with. Code would be as below.protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(measureWidth(widthMeasureSpec), measureHeight(heightMeasureSpec)); } private int measureHeight(int heightMeasureSpec) { int specMode = MeasureSpec.getMode(heightMeasureSpec); int specSize = MeasureSpec.getSize(heightMeasureSpec); int height; if (specMode == MeasureSpec.EXACTLY) height = specSize; else { height = mScaledDensity; if (specMode == MeasureSpec.AT_MOST) height = Math.min(height, specSize); } return height; } private measureWidth(int widthMeasureSpec) { int specMode = MeasureSpec.getMode(widthMeasureSpec); int specSize = MeasureSpec.getSize(widthMeasureSpec); int width; mTextWidth = specSize - getPaddingLeft() - mCheckOff.getMinimumWidth() - getPaddingRight(); if (specMode == MeasureSpec.EXACTLY) width = specSize; else { CharSequence temp = TextUtils.ellipsize(mLineOne, mTextPaint, mTextWidth, mEllipsize); width = (int) mTextPaint.measureText((String) temp) + getPaddingLeft() + getPaddingRight() + mCheckOff.getMinimumWidth(); if (specMode == MeasureSpec.AT_MOST) width = Math.min(width, specSize); } return width; }
onLayout
is called next, the inputs contains the absolute position of our view. This is good opportunity to calculate the coordinates of our controls. These coordinates will be used to eventually during onDraw
method.protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mViewWidth = right - left; mViewHeight = bottom - top; computeCooridates(); } private void computeCooridates() { int leftPadding = getPaddingLeft(); int rightPadding = getPaddingRight(); int bottomPadding = getPaddingBottom(); int topPadding = getPaddingTop(); mLineOneStyle.updateMeasureState(mTextPaint); int size = mCheckOff.getMinimumWidth(); mLineOneX = leftPadding; mLineOneY = topPadding - (int) mTextPaint.ascent(); mCheckboxX = mViewWidth - rightPadding - size; mCheckboxY = topPadding + mCheckboxTop; mCheckboxRect = new Rect(mCheckboxX, mCheckboxY, mCheckboxX + size, mCheckboxY + size); mLineTwoStyle.updateMeasureState(mTextPaint); int descent = (int) mTextPaint.descent(); mLineTwoX = leftPadding; mLineTwoY = mViewHeight - descent - bottomPadding; }
onDraw
. We draw the first line followed by the checkbox bitmap and lastly the second line. We have two bitmaps for the two states of the checkbox, based on the state of the checkbox we paint the appropriate bitmap.protected void onDraw(Canvas canvas) { super.onDraw(canvas); mLineOneStyle.updateDrawState(mTextPaint); mLineOneStyle.updateMeasureState(mTextPaint); String desc = (String) TextUtils.ellipsize(mLineOne, mTextPaint, mTextWidth, mEllipsize); canvas.drawText(desc, mLineOneX, mLineOneY, mTextPaint); BitmapDrawable bitmap = mIsChecked ? mCheckOn : mCheckOff; canvas.drawBitmap(bitmap.getBitmap(), null, mCheckboxRect, (Paint) mTextPaint); mLineTwoStyle.updateDrawState(mTextPaint); mLineTwoStyle.updateMeasureState(mTextPaint); canvas.drawText(mLineTwo, mLineTwoX, mLineTwoY, mTextPaint); }
onTouchEvent
method to respond to UI interactions by the user. Primarily we are concerned with MotionEvent.ACTION_DOWN
and MotionEvent.ACTION_UP
. On ACTION_DOWN, check if the touch coordinates fall within the range of the checkbox region, if yes, then set the flag. If the flag is set when we get ACTION_UP, then we have to toggle thepublic boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: int x = (int) event.getX(); if (x >= mCheckboxX) { mKeyDown = true; return true; } break; case MotionEvent.ACTION_UP: if (mKeyDown) { mKeyDown = false; toggleCheckbox(); return true; } break; } return super.onTouchEvent(event); } private void toggleCheckbox() { mIsChecked = !mIsChecked; if (mItemToggleListener != null) mItemToggleListener.toggle(mItemId, mIsChecked); postInvalidate(); }
OnItemClickListener
and OnItemToggleListener
. WhenonItemClick
is called handle single click and when onItemToggle
is called maintain our of map of selected items. Out of the box adapter implementations cannot handle a custom view, so finally we implement a custom adapter.