I've developed a custom component in which i've implemented onClick, onLongClick and onTouchListener. Everything is working fine except that onLongClick and onClick are called twice even i make action only once. What's wrong please guide towards solution. It takes a noticeable time in calling method again i.e. ~1 sec.
Following is the code:
CustomWidget:
#BindingMethods({
#BindingMethod(type = RecordingButton.class, attribute = "progress", method = "setProgress"),
})
public class RecordingButton extends RelativeLayout {
boolean isLongPressed;
boolean isPressed;
private Context mContext;
private int progressBackground, progressColor, progressBarWidth;
private int maxProgress, progress;
private LayoutRecordingBtnBinding itemViewBinding;
private OnRecordingButtonListener listener;
private ValueAnimator progressAnimator;
private View.OnTouchListener viewTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View pView, MotionEvent pEvent) {
pView.onTouchEvent(pEvent);
// We're only interested in when the button is released.
switch (pEvent.getAction()) {
case MotionEvent.ACTION_UP:
isPressed = false;
// We're only interested in anything if our speak button is currently pressed.
if (isLongPressed) {
AppLogger.d("usm_recording_button_2", "onTouch ACTION_UP is called to release longPress");
isLongPressed = false;
// Do something when the button is released.\
if (listener != null)
listener.onStopRecording();
} else {
setTintColor(R.color.fayvo_color);
}
break;
case MotionEvent.ACTION_DOWN:
isPressed = true;
setTintColor(R.color.trans_fayvo_color);
break;
}
return false;
}
};
public RecordingButton(Context context) {
super(context);
initializeView(context, null, 0);
}
public RecordingButton(Context context, AttributeSet attrs) {
super(context, attrs);
initializeView(context, attrs, 0);
}
public RecordingButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeView(context, attrs, defStyleAttr);
}
private void initializeView(Context context, AttributeSet attrs, int defStyleAttr) {
mContext = context;
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.RecordingButton, defStyleAttr, 0);
try {
maxProgress = array.getInteger(R.styleable.RecordingButton_android_max, 30);
progress = array.getInteger(R.styleable.RecordingButton_android_progress, 0);
progressBarWidth = array.getDimensionPixelSize(R.styleable.RecordingButton_progressWidth, -1);
progressColor = array.getColor(R.styleable.RecordingButton_progressColor, Integer.MAX_VALUE);
progressBackground = array.getColor(R.styleable.RecordingButton_progressBackgroundColor, Integer.MAX_VALUE);
} finally {
array.recycle();
}
itemViewBinding = LayoutRecordingBtnBinding.inflate(LayoutInflater.from(mContext), this, true);
setListeners();
}
private void setListeners() {
this.setOnClickListener(view -> {
// check if recording is not running on long press already
// if (progressAnimator != null && progressAnimator.isRunning())
// return;
// AppLogger.d("usm_loading_btn_1", "onClick is called");
if (listener != null) {
AppLogger.d("usm_recording_button_0", "onClick is called");
listener.onClick();
}
});
/*itemViewBinding.ivCameraBtn*/
this.setOnLongClickListener(pView -> {
AppLogger.d("usm_recording_button_1.0", "onLongClick detected: already isLongPressed= " + isLongPressed + " ,isPressed= " + isPressed);
if (isLongPressed /*|| !isPressed*/) // method is calling twice , need to do action only once
return true;
setTintColor(R.color.trans_fayvo_color);
// Do something when your hold starts here.
isLongPressed = true;
AppLogger.d("usm_recording_button_1.1", "onLongClick is called");
animateInnerIcon(true);
if (listener != null)
listener.onLongClick();
return true;
});
this.setOnTouchListener(viewTouchListener);
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
setValues();
}
private void setValues() {
try {
setProgress(progress);
setMaxProgress(maxProgress);
setProgressColor(progressColor);
setProgressBackgroundColor(progressBackground);
setProgressBarWidth(progressBarWidth);
} catch (Exception e) {
e.printStackTrace();
}
}
public void setProgress(int progress) {
itemViewBinding.circularProgressBar.setProgress(progress);
}
public void setMaxProgress(int maxProgress) {
itemViewBinding.circularProgressBar.setProgressMax(maxProgress);
}
public void setProgressBarWidth(int strokeWidth) {
if (strokeWidth > -1)
itemViewBinding.circularProgressBar.setProgressBarWidth(strokeWidth);
}
public void setProgressColor(int color) {
this.progressColor = color;
if (progressColor != Integer.MAX_VALUE) {
itemViewBinding.circularProgressBar.setColor(progressColor);
}
}
public void setProgressBackgroundColor(int color) {
this.progressBackground = color;
if (progressBackground != Integer.MAX_VALUE) {
itemViewBinding.circularProgressBar.setBackgroundColor(progressBackground);
}
}
public void setTintColor(int tintColor) {
// itemViewBinding.ivCameraBtn.setForegroundTintList(new ColorStateList()[]);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
itemViewBinding.ivCameraBtn.setImageTintList(ColorStateList.valueOf(ContextCompat.getColor(mContext, tintColor)));
}
}
public void animateInnerIcon(boolean expand) {
float from = expand ? 0f : 1f;
float to = expand ? 1f : 0f;
Animator scaleXAnimator = ObjectAnimator.ofFloat(itemViewBinding.ivInnerIcon, "scaleX", from, to);
Animator scaleYAnimator = ObjectAnimator.ofFloat(itemViewBinding.ivInnerIcon, "scaleY", from, to);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(scaleXAnimator, scaleYAnimator);
animatorSet.setInterpolator(new DecelerateInterpolator());
animatorSet.setDuration(expand ? 600 : 300);
animatorSet.start();
}
public void animateProgress() {
progressAnimator = ValueAnimator.ofFloat(0, maxProgress);
progressAnimator.addUpdateListener(animation -> {
try {
Float val = (Float) animation.getAnimatedValue();
itemViewBinding.circularProgressBar.setProgress(val);
} catch (Exception e) {
e.printStackTrace();
}
});
progressAnimator.setDuration(maxProgress * 1000);
progressAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
isLongPressed = false; // no action required on ActionUp of onTouch
if (listener != null)
listener.onStopRecording();
// reset();
}
});
progressAnimator.setInterpolator(new LinearInterpolator());
progressAnimator.start();
}
public void reset() {
AppLogger.d("usm_recording_button_2", "reset recording button");
if (progressAnimator != null)
progressAnimator.cancel();
itemViewBinding.circularProgressBar.setProgress(0);
/*itemViewBinding.ivInnerIcon.setScaleX(0f);
itemViewBinding.ivInnerIcon.setScaleY(0f);*/
if (itemViewBinding.ivInnerIcon.getScaleX() > 0)
animateInnerIcon(false);
}
public void setOnActionListener(OnRecordingButtonListener listener) {
this.listener = listener;
}
public interface OnRecordingButtonListener {
void onClick();
void onLongClick();
void onStopRecording();
}
}
Related
I am making a custom edittext to enter mobile numbers. Here is the code
public class PinEntryEditText extends android.support.v7.widget.AppCompatEditText {
private static final String XML_NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android";
protected String mMask = null;
protected StringBuilder mMaskChars = null;
protected String mSingleCharHint = null;
protected int mAnimatedType = 0;
protected float mSpace = 24; //24 dp by default, space between the lines
protected float mCharSize;
protected float mNumChars = 4;
protected float mTextBottomPadding = 8; //8dp by default, height of the text from our lines
protected int mMaxLength = 4;
protected RectF[] mLineCoords;
protected float[] mCharBottom;
protected Paint mCharPaint;
protected Paint mLastCharPaint;
protected Paint mSingleCharPaint;
protected Drawable mPinBackground;
protected Rect mTextHeight = new Rect();
protected boolean mIsDigitSquare = false;
protected View.OnClickListener mClickListener;
protected OnPinEnteredListener mOnPinEnteredListener = null;
protected float mLineStroke = 1; //1dp by default
protected float mLineStrokeSelected = 2; //2dp by default
protected Paint mLinesPaint;
protected boolean mAnimate = false;
protected boolean mHasError = false;
protected ColorStateList mOriginalTextColors;
protected int[][] mStates = new int[][]{
new int[]{android.R.attr.state_selected}, // selected
new int[]{android.R.attr.state_active}, // error
new int[]{android.R.attr.state_focused}, // focused
new int[]{-android.R.attr.state_focused}, // unfocused
};
protected int[] mColors = new int[]{
Color.GREEN,
Color.RED,
Color.BLACK,
Color.GRAY
};
protected ColorStateList mColorStates = new ColorStateList(mStates, mColors);
public PinEntryEditText(Context context) {
super(context);
}
public PinEntryEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public PinEntryEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
// this(context, attrs, android.R.attr.editTextStyle);
init(context, attrs);
}
public void setMaxLength(final int maxLength) {
mMaxLength = maxLength;
mNumChars = maxLength;
setFilters(new InputFilter[]{new InputFilter.LengthFilter(maxLength)});
setText(null);
invalidate();
}
private void init(Context context, AttributeSet attrs) {
float multi = context.getResources().getDisplayMetrics().density;
mLineStroke = multi * mLineStroke;
mLineStrokeSelected = multi * mLineStrokeSelected;
mSpace = multi * mSpace; //convert to pixels for our density
mTextBottomPadding = multi * mTextBottomPadding; //convert to pixels for our density
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.PinEntryEditText, 0, 0);
try {
TypedValue outValue = new TypedValue();
ta.getValue(R.styleable.PinEntryEditText_pinAnimationType, outValue);
mAnimatedType = outValue.data;
mMask = ta.getString(R.styleable.PinEntryEditText_pinCharacterMask);
mSingleCharHint = ta.getString(R.styleable.PinEntryEditText_pinRepeatedHint);
mLineStroke = ta.getDimension(R.styleable.PinEntryEditText_pinLineStroke, mLineStroke);
mLineStrokeSelected = ta.getDimension(R.styleable.PinEntryEditText_pinLineStrokeSelected, mLineStrokeSelected);
mSpace = ta.getDimension(R.styleable.PinEntryEditText_pinCharacterSpacing, mSpace);
mTextBottomPadding = ta.getDimension(R.styleable.PinEntryEditText_pinTextBottomPadding, mTextBottomPadding);
mIsDigitSquare = ta.getBoolean(R.styleable.PinEntryEditText_pinBackgroundIsSquare, mIsDigitSquare);
mPinBackground = ta.getDrawable(R.styleable.PinEntryEditText_pinBackgroundDrawable);
ColorStateList colors = ta.getColorStateList(R.styleable.PinEntryEditText_pinLineColors);
if (colors != null) {
mColorStates = colors;
}
} finally {
ta.recycle();
}
mCharPaint = new Paint(getPaint());
mLastCharPaint = new Paint(getPaint());
mSingleCharPaint = new Paint(getPaint());
mLinesPaint = new Paint(getPaint());
mLinesPaint.setStrokeWidth(mLineStroke);
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(R.attr.colorControlActivated,
outValue, true);
int colorSelected = outValue.data;
mColors[0] = colorSelected;
int colorFocused = isInEditMode() ? Color.GRAY : ContextCompat.getColor(context, R.color.pin_normal);
mColors[1] = colorFocused;
int colorUnfocused = isInEditMode() ? Color.GRAY : ContextCompat.getColor(context, R.color.pin_normal);
mColors[2] = colorUnfocused;
setBackgroundResource(0);
mMaxLength = attrs.getAttributeIntValue(XML_NAMESPACE_ANDROID, "maxLength", 4);
mNumChars = mMaxLength;
//Disable copy paste
super.setCustomSelectionActionModeCallback(new ActionMode.Callback() {
public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
return false;
}
public void onDestroyActionMode(ActionMode mode) {
}
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
return false;
}
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
return false;
}
});
// When tapped, move cursor to end of text.
super.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setSelection(getText().length());
if (mClickListener != null) {
mClickListener.onClick(v);
}
}
});
super.setOnLongClickListener(new OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
setSelection(getText().length());
return true;
}
});
//If input type is password and no mask is set, use a default mask
if ((getInputType() & InputType.TYPE_TEXT_VARIATION_PASSWORD) == InputType.TYPE_TEXT_VARIATION_PASSWORD && TextUtils.isEmpty(mMask)) {
mMask = "\u25CF";
} else if ((getInputType() & InputType.TYPE_NUMBER_VARIATION_PASSWORD) == InputType.TYPE_NUMBER_VARIATION_PASSWORD && TextUtils.isEmpty(mMask)) {
mMask = "\u25CF";
}
if (!TextUtils.isEmpty(mMask)) {
mMaskChars = getMaskChars();
}
//Height of the characters, used if there is a background drawable
getPaint().getTextBounds("|", 0, 1, mTextHeight);
mAnimate = mAnimatedType > -1;
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mOriginalTextColors = getTextColors();
if (mOriginalTextColors != null) {
mLastCharPaint.setColor(mOriginalTextColors.getDefaultColor());
mCharPaint.setColor(mOriginalTextColors.getDefaultColor());
mSingleCharPaint.setColor(getCurrentHintTextColor());
}
int availableWidth = getWidth() - ViewCompat.getPaddingEnd(this) - ViewCompat.getPaddingStart(this);
if (mSpace < 0) {
mCharSize = (availableWidth / (mNumChars * 2 - 1));
} else {
mCharSize = (availableWidth - (mSpace * (mNumChars - 1))) / mNumChars;
}
mLineCoords = new RectF[(int) mNumChars];
mCharBottom = new float[(int) mNumChars];
int startX;
int bottom = getHeight() - getPaddingBottom();
int rtlFlag;
final boolean isLayoutRtl = TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == ViewCompat.LAYOUT_DIRECTION_RTL;
if (isLayoutRtl) {
rtlFlag = -1;
startX = (int) (getWidth() - ViewCompat.getPaddingStart(this) - mCharSize);
} else {
rtlFlag = 1;
startX = ViewCompat.getPaddingStart(this);
}
for (int i = 0; i < mNumChars; i++) {
mLineCoords[i] = new RectF(startX, bottom, startX + mCharSize, bottom);
if (mPinBackground != null) {
if (mIsDigitSquare) {
mLineCoords[i].top = getPaddingTop();
mLineCoords[i].right = startX + mLineCoords[i].height();
} else {
mLineCoords[i].top -= mTextHeight.height() + mTextBottomPadding * 2;
}
}
if (mSpace < 0) {
startX += rtlFlag * mCharSize * 2;
} else {
startX += rtlFlag * (mCharSize + mSpace);
}
mCharBottom[i] = mLineCoords[i].bottom - mTextBottomPadding;
}
}
#Override
public void setOnClickListener(View.OnClickListener l) {
mClickListener = l;
}
#Override
public void setCustomSelectionActionModeCallback(ActionMode.Callback actionModeCallback) {
throw new RuntimeException("setCustomSelectionActionModeCallback() not supported.");
}
#Override
protected void onDraw(Canvas canvas) {
//super.onDraw(canvas);
CharSequence text = getFullText();
int textLength = text.length();
float[] textWidths = new float[textLength];
getPaint().getTextWidths(text, 0, textLength, textWidths);
float hintWidth = 0;
if (mSingleCharHint != null) {
float[] hintWidths = new float[mSingleCharHint.length()];
getPaint().getTextWidths(mSingleCharHint, hintWidths);
for (float i : hintWidths) {
hintWidth += i;
}
}
for (int i = 0; i < mNumChars; i++) {
//If a background for the pin characters is specified, it should be behind the characters.
if (mPinBackground != null) {
updateDrawableState(i < textLength, i == textLength);
mPinBackground.setBounds((int) mLineCoords[i].left, (int) mLineCoords[i].top, (int) mLineCoords[i].right, (int) mLineCoords[i].bottom);
mPinBackground.draw(canvas);
}
float middle = mLineCoords[i].left + mCharSize / 2;
if (textLength > i) {
if (!mAnimate || i != textLength - 1) {
canvas.drawText(text, i, i + 1, middle - textWidths[i] / 2, mCharBottom[i], mCharPaint);
} else {
canvas.drawText(text, i, i + 1, middle - textWidths[i] / 2, mCharBottom[i], mLastCharPaint);
}
} else if (mSingleCharHint != null) {
canvas.drawText(mSingleCharHint, middle - hintWidth / 2, mCharBottom[i], mSingleCharPaint);
}
//The lines should be in front of the text (because that's how I want it).
if (mPinBackground == null) {
updateColorForLines(i <= textLength);
canvas.drawLine(mLineCoords[i].left, mLineCoords[i].top, mLineCoords[i].right, mLineCoords[i].bottom, mLinesPaint);
}
}
}
private CharSequence getFullText() {
if (mMask == null) {
return getText();
} else {
return getMaskChars();
}
}
private StringBuilder getMaskChars() {
if (mMaskChars == null) {
mMaskChars = new StringBuilder();
}
int textLength = getText().length();
while (mMaskChars.length() != textLength) {
if (mMaskChars.length() < textLength) {
mMaskChars.append(mMask);
} else {
mMaskChars.deleteCharAt(mMaskChars.length() - 1);
}
}
return mMaskChars;
}
private int getColorForState(int... states) {
return mColorStates.getColorForState(states, Color.GRAY);
}
/**
* #param hasTextOrIsNext Is the color for a character that has been typed or is
* the next character to be typed?
*/
protected void updateColorForLines(boolean hasTextOrIsNext) {
if (mHasError) {
mLinesPaint.setColor(getColorForState(android.R.attr.state_active));
} else if (isFocused()) {
mLinesPaint.setStrokeWidth(mLineStrokeSelected);
mLinesPaint.setColor(getColorForState(android.R.attr.state_focused));
if (hasTextOrIsNext) {
mLinesPaint.setColor(getColorForState(android.R.attr.state_selected));
}
} else {
mLinesPaint.setStrokeWidth(mLineStroke);
mLinesPaint.setColor(getColorForState(-android.R.attr.state_focused));
}
}
protected void updateDrawableState(boolean hasText, boolean isNext) {
if (mHasError) {
mPinBackground.setState(new int[]{android.R.attr.state_active});
} else if (isFocused()) {
mPinBackground.setState(new int[]{android.R.attr.state_focused});
if (isNext) {
mPinBackground.setState(new int[]{android.R.attr.state_focused, android.R.attr.state_selected});
} else if (hasText) {
mPinBackground.setState(new int[]{android.R.attr.state_focused, android.R.attr.state_checked});
}
} else {
mPinBackground.setState(new int[]{-android.R.attr.state_focused});
}
}
public void setError(boolean hasError) {
mHasError = hasError;
}
public boolean isError() {
return mHasError;
}
/**
* Request focus on this PinEntryEditText
*/
public void focus() {
requestFocus();
// Show keyboard
InputMethodManager inputMethodManager = (InputMethodManager) getContext()
.getSystemService(Context.INPUT_METHOD_SERVICE);
inputMethodManager.showSoftInput(this, 0);
}
#Override
protected void onTextChanged(CharSequence text, final int start, int lengthBefore, final int lengthAfter) {
setError(false);
if (mLineCoords == null || !mAnimate) {
if (mOnPinEnteredListener != null && text.length() == mMaxLength) {
mOnPinEnteredListener.onPinEntered(text);
}
return;
}
if (mAnimatedType == -1) {
invalidate();
return;
}
if (lengthAfter > lengthBefore) {
if (mAnimatedType == 0) {
animatePopIn();
} else {
animateBottomUp(text, start);
}
}
}
private void animatePopIn() {
ValueAnimator va = ValueAnimator.ofFloat(1, getPaint().getTextSize());
va.setDuration(200);
va.setInterpolator(new OvershootInterpolator());
va.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
mLastCharPaint.setTextSize((Float) animation.getAnimatedValue());
PinEntryEditText.this.invalidate();
}
});
if (getText().length() == mMaxLength && mOnPinEnteredListener != null) {
va.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
mOnPinEnteredListener.onPinEntered(getText());
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
}
va.start();
}
private void animateBottomUp(CharSequence text, final int start) {
mCharBottom[start] = mLineCoords[start].bottom - mTextBottomPadding;
ValueAnimator animUp = ValueAnimator.ofFloat(mCharBottom[start] + getPaint().getTextSize(), mCharBottom[start]);
animUp.setDuration(300);
animUp.setInterpolator(new OvershootInterpolator());
animUp.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
Float value = (Float) animation.getAnimatedValue();
mCharBottom[start] = value;
PinEntryEditText.this.invalidate();
}
});
mLastCharPaint.setAlpha(255);
ValueAnimator animAlpha = ValueAnimator.ofInt(0, 255);
animAlpha.setDuration(300);
animAlpha.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
Integer value = (Integer) animation.getAnimatedValue();
mLastCharPaint.setAlpha(value);
}
});
AnimatorSet set = new AnimatorSet();
if (text.length() == mMaxLength && mOnPinEnteredListener != null) {
set.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animation) {
}
#Override
public void onAnimationEnd(Animator animation) {
mOnPinEnteredListener.onPinEntered(getText());
}
#Override
public void onAnimationCancel(Animator animation) {
}
#Override
public void onAnimationRepeat(Animator animation) {
}
});
}
set.playTogether(animUp, animAlpha);
set.start();
}
public void setAnimateText(boolean animate) {
mAnimate = animate;
}
public void setOnPinEnteredListener(OnPinEnteredListener l) {
mOnPinEnteredListener = l;
}
public interface OnPinEnteredListener {
void onPinEntered(CharSequence str);
}
}
Following is the xml
<com.cartoon.customLayout.PinEntryEditText
android:id="#+id/et_activity_mobile_pinEntryEditText"
android:layout_width="0dp"
android:layout_height="40dp"
android:layout_marginBottom="8dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:background="#null"
android:cursorVisible="true"
android:digits="1234567890"
android:focusable="true"
android:imeOptions="actionDone"
android:inputType="number|phone"
android:maxLength="10"
android:textColor="#android:color/white"
android:textIsSelectable="true"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.40"
app:pinAnimationType="fromBottom"
app:pinBackgroundDrawable="#drawable/bg_pin"
app:pinCharacterSpacing="4dp" />
Suppose the user enters his mobile number and the second digit entered is wrong then the problem is, he/she has to erase the entered characters leading to the second character for a change.
I went through the following posts but they are of no help
Custom EditText is not showing keyboard on focus
Custom Android pin code entry widget
https://github.com/ChaosLeong/PinView
Setting focusable, clickable or focusableInTouchMode does not work. Tried both in XML and code.
The below library does solve my problem but it has some other problem for which I have raised an issue
https://github.com/mukeshsolanki/android-otpview-pinview
Issue: https://github.com/mukeshsolanki/android-otpview-pinview/issues/26
Of course I could take 10 edittext and achieve the result which I want but that is not the right way and I will resort to that option in case I am not able to find the right solution to this one.
The answer is in your java code. As mentioned in the comment just remove this from the init() method.
// When tapped, move cursor to end of text.
super.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setSelection(getText().length());
if (mClickListener != null) {
mClickListener.onClick(v);
}
}
});
Please make the following changes:
In your PinEntryEditText class:
// When tapped, move cursor to end of text.
super.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//setSelection(getText().length());
if (mClickListener != null) {
mClickListener.onClick(v);
}
}
});
I think setSelection(getText().length()); was setting selection to the end of the EditText no matter where the click even occured.
And in your XML, do these: android:cursorVisible="true" and android:textIsSelectable="true"
I have custom view (extends LinearLayout) which contains RecyclerView. When I add new items RecyclerView doesn't change size. I think problem is that my custom view doesn`t give enough space to RecyclerView.
The question: How to change heigh of custom view depends on chlid recylerview size?
If I'm wrong, then correct me please
CustomView:
public class ExpandableView extends LinearLayout {
private Settings mSettings ;
private int mExpandState;
private ValueAnimator mExpandAnimator;
private ValueAnimator mParentAnimator;
private AnimatorSet mExpandScrollAnimatorSet;
private int mExpandedViewHeight;
private boolean mIsInit = true;
private boolean isAllowedExpand = false;
private ScrolledParent mScrolledParent;
private OnExpandListener mOnExpandListener;
public ExpandableView(Context context) {
super(context);
init(null);
}
public ExpandableView(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ExpandableView(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private void init(AttributeSet attrs) {
setOrientation(VERTICAL);
this.setClipChildren(false);
this.setClipToPadding(false);
mExpandState = ExpandState.PRE_INIT;
mSettings = new Settings();
if(attrs!=null) {
TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.ExpandableView);
mSettings.expandDuration = typedArray.getInt(R.styleable.ExpandableView_expDuration, Settings.EXPAND_DURATION);
mSettings.expandWithParentScroll = typedArray.getBoolean(R.styleable.ExpandableView_expWithParentScroll,false);
mSettings.expandScrollTogether = typedArray.getBoolean(R.styleable.ExpandableView_expExpandScrollTogether,true);
typedArray.recycle();
}
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int childCount = getChildCount();
if(childCount!=2) {
throw new IllegalStateException("ExpandableLayout must has two child view !");
}
if(mIsInit) {
((MarginLayoutParams)getChildAt(0).getLayoutParams()).bottomMargin=0;
MarginLayoutParams marginLayoutParams = ((MarginLayoutParams)getChildAt(1).getLayoutParams());
marginLayoutParams.bottomMargin=0;
marginLayoutParams.topMargin=0;
marginLayoutParams.height = 0;
mExpandedViewHeight = getChildAt(1).getMeasuredHeight();
mIsInit =false;
mExpandState = ExpandState.CLOSED;
View view = getChildAt(0);
if (view != null){
view.setOnClickListener(v -> toggle());
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
if(mSettings.expandWithParentScroll) {
mScrolledParent = Utils.getScrolledParent(this);
}
}
private int getParentScrollDistance () {
int distance = 0;
if(mScrolledParent == null) {
return distance;
}
distance = (int) (getY() + getMeasuredHeight() + mExpandedViewHeight - mScrolledParent.scrolledView.getMeasuredHeight());
for(int index = 0; index < mScrolledParent.childBetweenParentCount; index++) {
ViewGroup parent = (ViewGroup) getParent();
distance+=parent.getY();
}
return distance;
}
private void verticalAnimate(final int startHeight, final int endHeight ) {
int distance = getParentScrollDistance();
final View target = getChildAt(1);
mExpandAnimator = ValueAnimator.ofInt(startHeight,endHeight);
mExpandAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
target.getLayoutParams().height = (int) animation.getAnimatedValue();
target.requestLayout();
}
});
mExpandAnimator.addListener(new AnimatorListenerAdapter() {
#Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
if(endHeight-startHeight < 0) {
mExpandState = ExpandState.CLOSED;
if (mOnExpandListener != null) {
mOnExpandListener.onExpand(false);
}
} else {
mExpandState=ExpandState.EXPANDED;
if(mOnExpandListener != null) {
mOnExpandListener.onExpand(true);
}
}
}
});
//todo ??????????????????????
mExpandState=mExpandState==ExpandState.EXPANDED?ExpandState.CLOSING :ExpandState.EXPANDING;
mExpandAnimator.setDuration(mSettings.expandDuration);
if(mExpandState == ExpandState.EXPANDING && mSettings.expandWithParentScroll && distance > 0) {
mParentAnimator = Utils.createParentAnimator(mScrolledParent.scrolledView, distance, mSettings.expandDuration);
mExpandScrollAnimatorSet = new AnimatorSet();
if(mSettings.expandScrollTogether) {
mExpandScrollAnimatorSet.playTogether(mExpandAnimator,mParentAnimator);
} else {
mExpandScrollAnimatorSet.playSequentially(mExpandAnimator,mParentAnimator);
}
mExpandScrollAnimatorSet.start();
} else {
mExpandAnimator.start();
}
}
public void setExpand(boolean expand) {
if (mExpandState == ExpandState.PRE_INIT) {return;}
getChildAt(1).getLayoutParams().height=expand?mExpandedViewHeight:0;
requestLayout();
mExpandState=expand?ExpandState.EXPANDED:ExpandState.CLOSED;
}
public boolean isExpanded() {
return mExpandState==ExpandState.EXPANDED;
}
public void toggle() {
if (isAllowedExpand){
if(mExpandState==ExpandState.EXPANDED) {
close();
}else if(mExpandState==ExpandState.CLOSED) {
expand();
}
}
}
public void expand() {
verticalAnimate(0,mExpandedViewHeight);
}
public void close() {
verticalAnimate(mExpandedViewHeight,0);
}
public interface OnExpandListener {
void onExpand(boolean expanded) ;
}
public void setOnExpandListener(OnExpandListener onExpandListener) {
this.mOnExpandListener = onExpandListener;
}
public void setExpandScrollTogether(boolean expandScrollTogether) {
this.mSettings.expandScrollTogether = expandScrollTogether;
}
public void setExpandWithParentScroll(boolean expandWithParentScroll) {
this.mSettings.expandWithParentScroll = expandWithParentScroll;
}
public void setExpandDuration(int expandDuration) {
this.mSettings.expandDuration = expandDuration;
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if(mExpandAnimator!=null&&mExpandAnimator.isRunning()) {
mExpandAnimator.cancel();
mExpandAnimator.removeAllUpdateListeners();
}
if(mParentAnimator!=null&&mParentAnimator.isRunning()) {
mParentAnimator.cancel();
mParentAnimator.removeAllUpdateListeners();
}
if(mExpandScrollAnimatorSet!=null) {
mExpandScrollAnimatorSet.cancel();
}
}
public void setAllowedExpand(boolean allowedExpand) {
isAllowedExpand = allowedExpand;
}
public void refreshView(){
ViewGroup.LayoutParams params = this.getLayoutParams();
Log.d("tag", "params - " + params.height);
}
}
Specify your item height inside RecyclerView Adapter like below :
LinearLayout.LayoutParams relParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
relParams.height = 100;
relParams.width = Utility.getScreenWidth(mContext);
holder.yourDesireView.setLayoutParams(relParams);
Here is the screen width calculator method :
public static int getScreenWidth(Context context) {
if (context == null) {
return 0;
}
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return metrics.widthPixels;
}
I think it will be helpful...
I have a custom edittext control which has a clear (x) icon set on the right when it's in focus and has text. Clicking the clear icon removes the text from the textbox. Unfortunately, when you click into the textbox, the focus change event is fired infinitely, as changing the compound drawable within the focus change listener seems to fire off two more focus change events, the first with the focus off, and the second with the focus back on. Any idea how I can get this working without the infinite loop?
Here is the code:
public class CustomEditText : EditText {
private Drawable clearButton;
protected CustomEditText (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {
}
public CustomEditText (Context context) : base (context) {
Init ();
}
public CustomEditText (Context context, IAttributeSet attrs) : base (context, attrs) {
Init (attrs);
}
public CustomEditText (Context context, IAttributeSet attrs, int defStyle) : base (context, attrs, defStyle) {
Init (attrs);
}
protected void Init (IAttributeSet attrs = null) {
// Set up clear button
SetupClearButton ();
SetupEvents ();
}
private void SetupClearButton () {
clearButton = ContextCompat.GetDrawable (Android.App.Application.Context, Resource.Drawable.forms_edit_text_clear_gray);
clearButton.SetBounds (0, 0, clearButton.IntrinsicWidth, clearButton.IntrinsicHeight);
}
private void SetupEvents () {
// Handle clear button visibility
this.TextChanged += (sender, e) => {
if (this.HasFocus)
UpdateClearButton ();
};
this.FocusChange += (object sender, FocusChangeEventArgs e) => {
UpdateClearButton (e.HasFocus);// Gets called infinitely
};
// Handle clearing the text
this.Touch += (sender, e) => {
if (this.GetCompoundDrawables ()[2] != null &&
e.Event.Action == MotionEventActions.Up &&
e.Event.GetX () > (this.Width - this.PaddingRight - clearButton.IntrinsicWidth)) {
this.Text = "";
UpdateClearButton ();
e.Handled = true;
} else
e.Handled = false;
};
}
private void UpdateClearButton (bool hasFocus = true) {
var compoundDrawables = this.GetCompoundDrawables ();
var compoundDrawable = this.Text.Length == 0 || !hasFocus ? null : clearButton;
if (compoundDrawables[2] != compoundDrawable)
this.SetCompoundDrawables (compoundDrawables[0], compoundDrawables[1], compoundDrawable, compoundDrawables[3]);
}
}
I ported DroidParts' ClearableEditText to Xamarin.Android to use when using the Android's Support Library widgets were not appropriate.
Note: DroidParts is under Apache 2.0 license so I can not post my C# derivative in full to StackOverflow, but the key to avoiding the continuous focus changing is in the OnTouch and OnFocusChange methods and the fact that the listeners are added to the base EditText Widget.
Full Code # https://gist.github.com/sushihangover/01a7965aae75d8ef0589697aa8f0e750
public bool OnTouch(View v, MotionEvent e)
{
if (GetDisplayedDrawable() != null)
{
int x = (int)e.GetX();
int y = (int)e.GetY();
int left = (loc == Location.LEFT) ? 0 : Width - PaddingRight - xD.IntrinsicWidth;
int right = (loc == Location.LEFT) ? PaddingLeft + xD.IntrinsicWidth : Width;
bool tappedX = x >= left && x <= right && y >= 0 && y <= (Bottom - Top);
if (tappedX)
{
if (e.Action == MotionEventActions.Up)
{
Text = "";
if (listener != null)
{
listener.DidClearText();
}
}
return true;
}
}
if (l != null)
return l.OnTouch(v, e);
return false;
}
public void OnFocusChange(View v, bool hasFocus)
{
if (hasFocus)
SetClearIconVisible(!string.IsNullOrEmpty(Text));
else
SetClearIconVisible(false);
if (f != null)
f.OnFocusChange(v, hasFocus);
}
Original StackOverflow Q/A: How to create EditText with cross(x) button at end of it?
In my opinion, the easiest implementation I have is this :
public class ClearableEditext : EditText
{
Context mContext;
Drawable imgX;
public ClearableEditext(Context context) : base(context)
{
init(context, null);
}
public ClearableEditext(Context context, Android.Util.IAttributeSet attrs) : base(context, attrs)
{
init(context, attrs);
}
public ClearableEditext(Context context, Android.Util.IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
init(context, attrs);
}
public ClearableEditext(Context context, Android.Util.IAttributeSet attrs, int defStyleAttr, int defStyleRes) : base(context, attrs, defStyleAttr, defStyleRes)
{
init(context, attrs);
}
public void init(Context ctx, Android.Util.IAttributeSet attrs)
{
mContext = ctx;
imgX = ContextCompat.GetDrawable(ctx, Android.Resource.Drawable.PresenceOffline);
imgX.SetBounds(0, 0, imgX.IntrinsicWidth, imgX.IntrinsicHeight);
manageClearButton();
this.SetOnTouchListener(new TouchHelper(this, imgX));
this.AddTextChangedListener(new TextListener(this));
}
public void manageClearButton()
{
if (this.Text.ToString().Equals(""))
removeClearButton();
else
addClearButton();
}
public void addClearButton()
{
this.SetCompoundDrawables(this.GetCompoundDrawables()[0],
this.GetCompoundDrawables()[1],
imgX,
this.GetCompoundDrawables()[3]);
}
public void removeClearButton()
{
this.SetCompoundDrawables(this.GetCompoundDrawables()[0],
this.GetCompoundDrawables()[1],
null,
this.GetCompoundDrawables()[3]);
}
}
public class TouchHelper : Java.Lang.Object, View.IOnTouchListener
{
ClearableEditext Editext;
public ClearableEditext objClearable { get; set; }
Drawable imgX;
public TouchHelper(ClearableEditext editext, Drawable imgx)
{
Editext = editext;
objClearable = objClearable;
imgX = imgx;
}
public bool OnTouch(View v, MotionEvent e)
{
ClearableEditext et = Editext;
if (et.GetCompoundDrawables()[2] == null)
return false;
// Only do this for up touches
if (e.Action != MotionEventActions.Up)
return false;
// Is touch on our clear button?
if (e.GetX() > et.Width - et.PaddingRight - imgX.IntrinsicWidth)
{
Editext.Text = string.Empty;
if (objClearable != null)
objClearable.removeClearButton();
}
return false;
}
}
public class TextListener : Java.Lang.Object, ITextWatcher
{
public ClearableEditext objClearable { get; set; }
public TextListener(ClearableEditext objRef)
{
objClearable = objRef;
}
public void AfterTextChanged(IEditable s)
{
}
public void BeforeTextChanged(ICharSequence s, int start, int count, int after)
{
}
public void OnTextChanged(ICharSequence s, int start, int before, int count)
{
if (objClearable != null)
objClearable.manageClearButton();
}
}
Probably Sushi has a better answer but i would suggest you to try this one out.
To change the x icon as your custom one change the image in init()
I want to create a animation in android with Gridview. The animation will be when I will change the number of columns from 2 to 4.
I used the following line to change the number of columns:
If (true)
gridView.setNumColumns(2);
Else
gridView.setNumColumns(4);
I want to achieve animation like this:
https://www.youtube.com/watch?v=1NkuChdWA_I
I need it too and then i created the following class:
/**
* Created by Butzke on 19/05/2017.
*/
public class GridViewAnimated extends GridView {
private int animationDuration = 300, nextColumns;
private boolean animating = false, waitingScroll, shouldWait = true;
private GridViewAnimationListener animationListener;
public GridViewAnimated(Context context) {
super(context);
init();
}
public GridViewAnimated(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public GridViewAnimated(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#TargetApi(21)
public GridViewAnimated(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
#Override
public int computeVerticalScrollOffset() {
return super.computeVerticalScrollOffset();
}
private void init() {
this.setStretchMode(GridView.STRETCH_COLUMN_WIDTH);
}
public void setAnimating(boolean animating) {
this.animating = animating;
}
#Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
if (waitingScroll) {
super.onScrollChanged(l, t, oldl, oldt);
if (computeVerticalScrollOffset() == 0) {
setEnabled(true);
waitingScroll = false;
setAnimating(false);
setNumColumns(nextColumns);
}
} else if (!animating) {
super.onScrollChanged(l, t, oldl, oldt);
}
}
public int getAnimationDuration() {
return animationDuration;
}
public void setAnimationDuration(int animationDuration) {
this.animationDuration = animationDuration;
}
public interface GridViewAnimationListener {
void onAnimationStart(Animator animator);
void onAnimationEnd(Animator animator);
void onAnimationCancel(Animator animator);
void onAnimationRepeat(Animator animator);
}
public GridViewAnimationListener getAnimationListener() {
return animationListener;
}
public void setAnimationListener(GridViewAnimationListener animationListener) {this.animationListener = animationListener;}
public void removeAt(int... positions) {
ArrayList<MyInteger> ints = new ArrayList<>();
for (Integer pos : positions) {
ints.add(new MyInteger(pos));
}
Collections.sort(ints);
int lowest = 99999, highest = 0;
for (MyInteger pos : ints) {
lowest = lowest < pos.getValue() ? lowest : pos.getValue();
highest = highest > pos.getValue() ? highest : pos.getValue();
}
if (lowest >= 0 && highest <= getChildCount()) {
ViewsVO originalViews = getOriginalViews();
for (MyInteger pos : ints) {
getAdapter().removeAt(pos.getValue());
}
getAdapter().notifyDataSetChanged();
animateChildViews(originalViews);
}
}
#Override
public void setAdapter(ListAdapter adapter) {
if (adapter instanceof GridViewAnimatedAdapter) {
super.setAdapter(adapter);
} else {
Log.e("GridViewAnimated", "Adapter needs to be instance of GridViewAnimatedAdapter");
Toast.makeText(getContext(), "Adapter needs to be instance of GridViewAnimatedAdapter", Toast.LENGTH_SHORT).show();
}
}
#Override
public GridViewAnimatedAdapter getAdapter() {
return (GridViewAnimatedAdapter) super.getAdapter();
}
#Override
public void setNumColumns(int numColumns) {
if (getAdapter() == null || !getAdapter().hasStableIds() || getChildCount() == 0) {
super.setNumColumns(numColumns);
setAnimating(false);
return;
} else if (animating) {
return;
}
setAnimating(true);
if (computeVerticalScrollOffset() > 0) {
waitingScroll = true;
setEnabled(false);
smoothScrollToPosition(0);
nextColumns = numColumns;
return;
}
ViewsVO originalViews = getOriginalViews();
super.setNumColumns(numColumns);
getAdapter().notifyDataSetChanged();
animateChildViews(originalViews);
}
private ViewsVO getOriginalViews() {
ViewsVO originalViews = new ViewsVO();
for (int i = 0; i < getChildCount(); i++) {
originalViews.addView(getChildAt(i));
}
return originalViews;
}
private void animateChildViews(final ViewsVO originalViews) {
final GridViewAnimated gridView = this;
getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
gridView.getViewTreeObserver().removeOnPreDrawListener(this);
ViewsVO newViews = new ViewsVO();
if (Build.VERSION.SDK_INT >= 21) {
for (int i = 0, z = getChildCount() - 1; z >= 0; z--, i++) {
gridView.getChildAt(z).setTranslationZ(i);
}
}
boolean hasFirst = false;
View firstView = null, lastView = null;
float firstHeight = 0, lastHeight = 0;
ViewsVO newViewsFirstTime = new ViewsVO();
for (int i = 0; i < getChildCount(); i++) {
View view = getChildAt(i);
ViewVO nView = newViews.addView(view);
ViewVO oView = originalViews.getView(view.getId());
if (oView != null) {
view.setScaleX(getScaleX(oView, nView));
view.setScaleY(getScaleY(oView, nView));
view.setTranslationX(getTranslateX(oView, nView));
view.setTranslationY(getTranslateY(oView, nView));
if (!hasFirst) {
firstView = view;
firstHeight = oView.getHeight();
hasFirst = true;
}
lastView = view;
lastHeight = oView.getHeight();
animateView(view);
} else {
newViewsFirstTime.addView(nView);
}
}
for (int i = 0; i < newViewsFirstTime.size(); i++) {
try {
View view = newViewsFirstTime.getViews().get(i).getView();
view.getId();
view.setScaleX(view.getId() > firstView.getId() ? firstView.getScaleX() : lastView.getScaleX());
view.setScaleY(view.getId() > firstView.getId() ? firstView.getScaleY() : lastView.getScaleX());
view.setTranslationX(view.getId() > firstView.getId() ? firstView.getTranslationX() : lastView.getTranslationX());
view.setTranslationY(view.getId() > firstView.getId() ? 0 - firstHeight : lastView.getTranslationY() + lastHeight);
animateView(view);
} catch (Exception ex) {
ex.printStackTrace();
}
}
return false;
}
});
}
private void animateView(final View view) {
ViewPropertyAnimator animator = view.animate().setDuration(animationDuration).translationX(0).translationY(0).scaleX(1).scaleY(1).setListener(new Animator.AnimatorListener() {
#Override
public void onAnimationStart(Animator animator) {animationListener.onAnimationStart(animator);}
#Override
public void onAnimationEnd(Animator animator) {
setAnimating(false);
animationListener.onAnimationEnd(animator);
ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
params.height = ViewGroup.LayoutParams.MATCH_PARENT;
view.setLayoutParams(params);
}
#Override
public void onAnimationCancel(Animator animator) {animationListener.onAnimationCancel(animator);}
#Override
public void onAnimationRepeat(Animator animator) {animationListener.onAnimationRepeat(animator);}
});
if (Build.VERSION.SDK_INT >= 21) {
animator.translationZ(0);
}
}
private float getTranslateX(ViewVO ov, ViewVO nv) {return ov.getPosX() - nv.getPosX() - ((ov.getWidth() < nv.getWidth() ? nv.getWidth() - ov.getWidth() : ov.getWidth() - nv.getWidth()) * (ov.getWidth() < nv.getWidth() ? 0.5f : -0.5f));}
private float getTranslateY(ViewVO ov, ViewVO nv) {return ov.getPosY() - nv.getPosY() - ((ov.getHeight() < nv.getHeight() ? nv.getHeight() - ov.getHeight() : ov.getHeight() - nv.getHeight()) * (ov.getHeight() < nv.getHeight() ? 0.5f : -0.5f));}
private float getScaleY(ViewVO ov, ViewVO nv) {
return ov.getHeight() / nv.getHeight();
}
private float getScaleX(ViewVO ov, ViewVO nv) {
return ov.getWidth() / nv.getWidth();
}
private class ViewsVO {
private ArrayList<ViewVO> views;
private int size() {
return views.size();
}
private ViewsVO() {
views = new ArrayList<>();
}
private ViewVO addView(View view) {
ViewVO v = new ViewVO(view);
addView(v);
return v;
}
private void addView(ViewVO view) {
views.add(view);
}
private ViewVO getView(long id) {
for (ViewVO view : views) {
if (view.getId() == id) {
return view;
}
}
return null;
}
private ArrayList<ViewVO> getViews() {
return views;
}
}
private class ViewVO {
private View view;
private int id;
private float posY;
private float posX;
private float width;
private float height;
private ViewVO(View view) {
this.view = view;
id = view.getId();
posX = view.getLeft();
posY = view.getTop();
width = view.getWidth();
height = view.getHeight();
}
public int getId() {
return id;
}
public View getView() {
return view;
}
private float getWidth() {
return width;
}
private float getHeight() {
return height;
}
private float getPosY() {
return posY;
}
private float getPosX() {
return posX;
}
}
public static class GridViewAnimatedAdapter extends ArrayAdapter {
private List objects;
public GridViewAnimatedAdapter(Context context, int resource, List objects) {
super(context, resource, objects);
this.objects = objects;
}
public void removeAt(int pos) {objects.remove(pos);}
#Override
public boolean hasStableIds() {
return true;
}
public View dealViewToAnimation(View view) {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.width = ViewGroup.LayoutParams.MATCH_PARENT;
params.height = ViewGroup.LayoutParams.MATCH_PARENT;
view.setLayoutParams(params);
return view;
}
}
private class MyInteger implements Comparable<MyInteger> {
private int value;
public MyInteger(int value) {
this.value = value;
}
public int getValue() {
return value;
}
public void setValue(int value) {
this.value = value;
}
#Override
public int compareTo(MyInteger another) {return value > another.getValue() ? -1 : (value == another.getValue() ? 0 : 1);}}
}
And to use it is quite simple,
The adapter of GridView needs to extends GridViewAnimatedAdapter and:
getView(): To set an ID to each view and in the end of the method getView, needs to finish using the method dealViewToAnimation(View view) Ex:
#Override
public View getView(int position, View view, ViewGroup parent) {
...
view.setId(images.getId());
...
return dealViewToAnimation(view);
}
You can also set a listener to animation status:
gridView.setAnimationListener(new GridViewAnimated.GridViewAnimationListener() {
#Override
public void onAnimationStart(Animator animator) {
mListMenu.setVisible(false);
}
#Override
public void onAnimationEnd(Animator animator) {
mListMenu.setVisible(true);
gridView.setAnimating(false);
}
#Override
public void onAnimationCancel(Animator animator) {}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
With this GridView you can animate changes in column number and animate removal of itens...
Animating change of column number:
gridView.setNumColumns(5);
Animating removal of itens:
gridView.removeAt(2, 7, 4);
gridView.removeAt(2);
When more than one, doesn't matter the order, the method will check the order and remove from the highest to lowest, considering that the highest id (that image added to the view) is the first view...
But despite it doesn't have the scale proportion animation like in the GridViewAnimated, it's better with RecyclerView, GridLayoutManager and DefaultItemAnimator, since it changes columns number and doesn't scroll...
GridLayoutmanager gridLayoutManager = new GridLayoutManager(context, 2);
recyclerView.setLayoutManager(gridLayoutManager);
recyclerView.setItemAnimator(new DefaultItemAnimator());
And then when you need to change the columns:
gridLayoutManager.requestSimpleAnimationsInNextLayout();
gridLayoutManager.setSpanCount(newNumberOfColumns);
I am using touch listener for an imageview in the listview item .It is working fine when I just do that like an sample application(I mean I have that Listview item as an sample one). But when I put this in a listview it is really becoming very slow.
I just wants to drag the image only horizantally,for that purpose I used ConstrainedDragandDrop View class which I got from the Github.
In my sample application:
public class HorizontalScroll extends Activity {
ImageView full_left,heart;
RelativeLayout rlMainLayout,rlImages,Cartoon_image,half_left,play_btn,centre,centre_leftanimate;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.inflated_bloops);
half_left=(RelativeLayout)findViewById(R.id.half_left);
Cartoon_image=(RelativeLayout)findViewById(R.id.cartoon_image);
play_btn=(RelativeLayout)findViewById(R.id.play_btn);
heart=(ImageView)findViewById(R.id.ivheart);
ConstrainedDragAndDropView dndView = (ConstrainedDragAndDropView) findViewById(R.id.dndView);
dndView.setDragHandle(findViewById(R.id.cartoon_image),findViewById(R.id.half_left),heart);
dndView.setAllowVerticalDrag(false,HorizontalScroll.this);
}
}
When I used in my Listview:
public class Cars extends BaseAdapter
{
public View getView(int position, View convertView, ViewGroup parent)
{
View v = convertView;
LayoutInflater inflator = getLayoutInflater();
v= inflator.inflate(R.layout.inflated_bloops, null);
ConstrainedDragAndDropView dndView = (ConstrainedDragAndDropView) v.findViewById(R.id.dndView);
dndView.setDragHandle(v.findViewById(R.id.cartoon_image),v.findViewById(R.id.half_left),heart);
dndView.setAllowVerticalDrag(false,FeedModeActivity.this);
RelativeLayout Cartoon_image=(RelativeLayout)v.findViewById(R.id.cartoon_image);
ImageView ivDummy =(ImageView)v.findViewById(R.id.dummy);
try{
loader.DisplayImageRelative( al_new.get(position).replace(" ", "%20"),ivDummy, Cartoon_image);
}
catch(Exception e){
e.printStackTrace();
}
return v;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return al_new.size();
}
#Override
public Object getItem(int arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
public long getItemId(int position) {
// TODO Auto-generated method stub
return 0;
}
}
Here is my custom dragDrop view:
public class ConstrainedDragAndDropView extends LinearLayout {
Activity main;
protected View dragHandle,half,heart;
protected List<View> dropTargets = new ArrayList<View>();
protected boolean dragging = false;
protected int pointerId;
protected int selectedDropTargetIndex = -1;
protected int lastSelectedDropTargetIndex = -1;
protected int lastDroppedIndex = -1;
protected boolean allowHorizontalDrag = true;
protected boolean allowVerticalDrag = true;
protected DropListener dropListener;
public interface DropListener {
public void onDrop(final int dropIndex, final View dropTarget);
}
public ConstrainedDragAndDropView(Context context) {
super(context);
}
public ConstrainedDragAndDropView(Context context, AttributeSet attrs) {
super(context, attrs);
applyAttrs(context, attrs);
}
#SuppressLint("NewApi")
public ConstrainedDragAndDropView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
applyAttrs(context, attrs);
}
public DropListener getDropListener() {
return dropListener;
}
public void setDropListener(DropListener dropListener) {
this.dropListener = dropListener;
}
public View getDragHandle() {
return dragHandle;
}
public void setDragHandle(View dragHandle,View half_left,View heart) {
this.dragHandle = dragHandle;
this.half=half_left;
this.heart=heart;
setupDragHandle();
}
public List<View> getDropTargets() {
return dropTargets;
}
public void setDropTargets(List<View> dropTargets) {
this.dropTargets = dropTargets;
}
public void addDropTarget(View target) {
if (dropTargets == null) {
dropTargets = new ArrayList<View>();
}
dropTargets.add(target);
}
public boolean isAllowHorizontalDrag() {
return allowHorizontalDrag;
}
public void setAllowHorizontalDrag(boolean allowHorizontalDrag) {
this.allowHorizontalDrag = allowHorizontalDrag;
}
public boolean isAllowVerticalDrag() {
return allowVerticalDrag;
}
public void setAllowVerticalDrag(boolean allowVerticalDrag,Activity c) {
this.allowVerticalDrag = allowVerticalDrag;
this.main=c;
}
protected void applyAttrs(Context context, AttributeSet attrs) {
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ConstrainedDragAndDropView, 0, 0);
try {
/*
layoutId = a.getResourceId(R.styleable.ConstrainedDragAndDropView_layoutId, 0);
if (layoutId > 0) {
LayoutInflater.from(context).inflate(layoutId, this, true);
}
*/
} finally {
a.recycle();
}
}
protected void setupDragHandle() {
this.setOnTouchListener(new DragAreaTouchListener());
}
protected class DragAreaTouchListener implements OnTouchListener {
public boolean onTouch(View view, MotionEvent motionEvent) {
switch (motionEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d("Action down", "action down in listener");
onActionDown(view, motionEvent);
break;
case MotionEvent.ACTION_UP:
Log.d("Action up", "action up in listener");
onActionUp(view, motionEvent);
break;
case MotionEvent.ACTION_MOVE:
Log.d("Action move", "action move in listener");
onActionMove(view, motionEvent);
break;
default:
break;
}
return true;
}
}
public static int param;
protected void onActionDown(View view, MotionEvent motionEvent) {
// if we're not already dragging, and the touch position is on the drag handle,
// then start dragging
if(!dragging && isDragHandleTouch(motionEvent)) {
pointerId = motionEvent.getPointerId(0);
Float s1= dragHandle.getX();
param=Integer.valueOf(s1.intValue());
Log.e("param",""+param);
// updateDragPosition(motionEvent);
dragging = true;
Log.d("drag", "drag start");
}
}
protected void onActionUp(View view, MotionEvent motionEvent) {
Log.d("Action up", "action up");
// if we're dragging, then stop dragging
if (dragging && motionEvent.getPointerId(0) == pointerId) {
Log.e("condition","satisfied");
Log.e("x val"+(int)motionEvent.getX(),"y val"+(int)motionEvent.getY());
Log.e("x1"+half.getLeft(),"y1"+half.getTop());
Log.e("x4"+half.getRight(),"y4"+half.getBottom());
Float s1=motionEvent.getX();
Float s2=motionEvent.getY();
int x=Integer.valueOf(s1.intValue());
dragging = false;
int y=Integer.valueOf(s2.intValue());
if((half.getLeft()<x)&&(x<half.getRight())&&((half.getTop()<y)&&(y<half.getBottom())))
{
Log.e("drop","on target");
try {
Thread.sleep(1000);
dragHandle.setX(param);
heart.setVisibility(View.VISIBLE);
Animation pulse = AnimationUtils.loadAnimation(main, R.anim.pulse);
heart.startAnimation(pulse);
// heart.setVisibility(View.GONE);
pulse.setAnimationListener(new AnimationListener() {
public void onAnimationStart(Animation animation) {
// TODO Auto-generated method stub
Log.e("animation","start");
}
public void onAnimationRepeat(Animation animation) {
// TODO Auto-generated method stub
}
public void onAnimationEnd(Animation animation) {
// TODO Auto-generated method stub
Log.e("animation","end");
heart.setVisibility(View.GONE);
}
});
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// class SimpleThread extends Thread {
//
// public void run() {
//
// try {
// sleep(1000);
// runOnUiThread(new Runnable() {
// public void run() {
// dragHandle.setX(param);
// heart.setVisibility(View.VISIBLE);
//
// Animation pulse = AnimationUtils.loadAnimation(main, R.anim.pulse);
// heart.startAnimation(pulse);
// }
// });
//
// }
// catch (InterruptedException e) {
// // TODO Auto-generated catch block
// e.printStackTrace();
// }
//
// }
// }
//
// new SimpleThread().start();
}
else{
Log.e("drop",""+"outside target");
dragHandle.setX(param);
// TranslateAnimation transAnimation= new TranslateAnimation(x, 300, y, 300);
// transAnimation.setDuration(1000);//set duration
// view.startAnimation(transAnimation);
}
// if()
// updateDragPosition(motionEvent);
Log.d("drag", "drag end");
// find out what drop target, if any, the drag handle was dropped on
int dropTargetIndex = findDropTargetIndexUnderDragHandle();
if(dropTargetIndex >= 0) { // if drop was on a target, select the target
Log.d("drag", "drop on target " + dropTargetIndex);
// selectDropTarget(dropTargetIndex);
// snapDragHandleToDropTarget(dropTargetIndex);
// lastDroppedIndex = dropTargetIndex;
if(dropListener != null) {
dropListener.onDrop(dropTargetIndex, dropTargets.get(dropTargetIndex));
}
} else { // if drop was not on a target, re-select the last selected target
// deselectDropTarget();
// snapDragHandleToDropTarget(lastDroppedIndex);
}
}
else{
Log.e("condition not","satisfied");
}
}
protected void onActionMove(View view, MotionEvent motionEvent) {
Log.d("Action move", "action move");
if (dragging && motionEvent.getPointerId(0) == pointerId) {
updateDragPosition(motionEvent);
int dropTargetIndex = findDropTargetIndexUnderDragHandle();
if(dropTargetIndex >= 0) {
Log.d("drag", "hover on target " + dropTargetIndex);
// selectDropTarget(dropTargetIndex);
} else {
// deselectDropTarget();
}
}
}
#SuppressLint("NewApi")
protected void updateDragPosition(MotionEvent motionEvent) {
// this is where we constrain the movement of the dragHandle
if(allowHorizontalDrag) {
float candidateX = motionEvent.getX() - dragHandle.getWidth() / 2;
if(candidateX > 0 && candidateX + dragHandle.getWidth() < this.getWidth()) {
dragHandle.setX(candidateX);
}
}
if(allowVerticalDrag) {
float candidateY = motionEvent.getY() - dragHandle.getHeight() / 2;
if(candidateY > 0 && candidateY + dragHandle.getHeight() < this.getHeight()) {
dragHandle.setY(candidateY);
}
}
}
#SuppressLint("NewApi")
protected void snapDragHandleToDropTarget(int dropTargetIndex) {
if(dropTargetIndex > -1) {
View dropTarget = dropTargets.get(dropTargetIndex);
float xCenter = dropTarget.getX() + dropTarget.getWidth() / 2;
float yCenter = dropTarget.getY() + dropTarget.getHeight() / 2;
float xOffset = dragHandle.getWidth() / 2;
float yOffset = dragHandle.getHeight() / 2;
float x = xCenter - xOffset;
float y = yCenter - yOffset;
dragHandle.setX(x);
dragHandle.setY(y);
}
}
protected boolean isDragHandleTouch(MotionEvent motionEvent) {
Point point = new Point(
new Float(motionEvent.getRawX()).intValue(),
new Float(motionEvent.getRawY()).intValue()
);
return isPointInView(point, dragHandle);
}
protected int findDropTargetIndexUnderDragHandle() {
int dropTargetIndex = -1;
for(int i = 0; i < dropTargets.size(); i++) {
if(isCollision(dragHandle, dropTargets.get(i))) {
dropTargetIndex = i;
break;
}
}
return dropTargetIndex;
}
/**
* Determines whether a raw screen coordinate is within the bounds of the specified view
* #param point - Point containing screen coordinates
* #param view - View to test
* #return true if the point is in the view, else false
*/
protected boolean isPointInView(Point point, View view) {
int[] viewPosition = new int[2];
view.getLocationOnScreen(viewPosition);
int left = viewPosition[0];
int right = left + view.getWidth();
int top = viewPosition[1];
int bottom = top + view.getHeight();
return point.x >= left && point.x <= right && point.y >= top && point.y <= bottom;
}
#SuppressLint("NewApi")
protected boolean isCollision(View view1, View view2) {
boolean collision = false;
do {
if(view1.getY() + view1.getHeight() < view2.getY()) {
break;
}
if(view1.getY() > view2.getY() + view2.getHeight()) {
break;
}
if(view1.getX() > view2.getX() + view2.getWidth()) {
break;
}
if(view1.getX() + view1.getWidth() < view2.getX()) {
break;
}
collision = true;
} while(false);
return collision;
}
protected void selectDropTarget(int index) {
if(index > -1) {
deselectDropTarget();
selectedDropTargetIndex = index;
dropTargets.get(selectedDropTargetIndex).setSelected(true);
}
}
protected void deselectDropTarget() {
if(selectedDropTargetIndex > -1) {
dropTargets.get(selectedDropTargetIndex).setSelected(false);
lastSelectedDropTargetIndex = selectedDropTargetIndex;
selectedDropTargetIndex = -1;
}
}
}