I'm a beginner android developer and trying to create a Custom Pin Edittext using the following code and i want to set the color of the pin to transparent if filled,unfortunately there is no state_filled in the attr of android, how should i do that? Help me please here is my code.
public class PinEntryEditText extends AppCompatEditText {
public static final String XML_NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android";
private float mSpace = 24; //24 dp by default, space between the lines
private float mCharSize;
private float mNumChars = 6;
private float mLineSpacing = 8; //8dp by default, height of the text from our lines
private int mMaxLength = 6;
private OnClickListener mClickListener;
private float mLineStroke = 1; //1dp by default
private float mLineStrokeSelected = 2; //2dp by default
private Paint mLinesPaint;
int[][] mStates = new int[][]{
new int[]{android.R.attr.state_selected}, // selected
new int[]{android.R.attr.state_focused}, // focused
new int[]{-android.R.attr.state_focused}, // unfocused
};
//Green color = 0xFFB6C800
//Gray color = 0xFFCCCCCC
int[] mColors = new int[]{
0xFFB6C800,
0xFFCCCCCC,
0xFF880000,
};
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);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
float multi = context.getResources().getDisplayMetrics().density;
mLineStroke = multi * mLineStroke;
mLineStrokeSelected = multi * mLineStrokeSelected;
mLinesPaint = new Paint(getPaint());
mLinesPaint.setStrokeWidth(mLineStroke);
setBackgroundResource(0);
mSpace = multi * mSpace; //convert to pixels for our density
mLineSpacing = multi * mLineSpacing; //convert to pixels for our density
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 OnClickListener() {
#Override
public void onClick(View v) {
setSelection(getText().length());
if (mClickListener != null) {
mClickListener.onClick(v);
}
}
});
}
#Override
public void setOnClickListener(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);
int availableWidth = getWidth() - getPaddingRight() - getPaddingLeft();
if (mSpace < 0) {
mCharSize = (availableWidth / (mNumChars * 2 - 1));
} else {
mCharSize = (availableWidth - (mSpace * (mNumChars - 1))) / mNumChars;
}
int startX = getPaddingLeft();
int bottom = getHeight() - getPaddingBottom();
//Text Width
Editable text = getText();
int textLength = text.length();
float[] textWidths = new float[textLength];
getPaint().getTextWidths(getText(), 0, textLength, textWidths);
for (int i = 0; i < mNumChars; i++) {
updateColorForLines(i == textLength);
canvas.drawLine(startX, bottom, startX + mCharSize, bottom, mLinesPaint);
if (getText().length() > i) {
float middle = startX + mCharSize / 2;
canvas.drawText(text, i, i + 1, middle - textWidths[0] / 2, bottom - mLineSpacing, getPaint());
}
if (mSpace < 0) {
startX += mCharSize * 2;
} else {
startX += mCharSize + mSpace;
}
}
//mLinesPaint.setColor(getColorForState(0xff1a1f71));
}
private int getColorForState(int... states) {
return mColorStates.getColorForState(states, Color.GRAY);
}
/**
* #param next Is the current char the next character to be input?
*/
private void updateColorForLines(boolean next) {
if (isFocused()) {
mLinesPaint.setStrokeWidth(mLineStrokeSelected);
mLinesPaint.setColor(getColorForState(android.R.attr.state_focused));
if (next) {
mLinesPaint.setColor(getColorForState(android.R.attr.state_selected));
}
} else {
mLinesPaint.setStrokeWidth(mLineStroke);
mLinesPaint.setColor(getColorForState(-android.R.attr.state_focused));
}
}
}
Is there a way to do it?
there is no state_filled but you can create one. you can use TextWatcher interface it will be triggered whenever text will be updated. you can check there if current text size is equal to your mMaxLength set your pin color to transparent or else.
here is an example of TextWatcher implementation :
et1.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// TODO Auto-generated method stub
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// TODO Auto-generated method stub
}
#Override
public void afterTextChanged(Editable s) {
// TODO Auto-generated method stub
}
});
Related
Following this question
RecyclerView ItemTouchHelper Buttons on Swipe
(NOT THE SAME). I would like to add buttons on each side.
The official answer from that link is for ONE side only.
What I have tried:
public abstract class SwipeHelper extends ItemTouchHelper.SimpleCallback {
public static final int BUTTON_WIDTH = 125;
private RecyclerView recyclerView;
private List<UnderlayButton> buttons;
private GestureDetector gestureDetector;
private GestureDetector gestureDetector2;
private int swipedPos = -1;
private float swipeThreshold = 0.5f;
private Map<Integer, List<UnderlayButton>> buttonsBuffer;
private Queue<Integer> recoverQueue;
private GestureDetector.SimpleOnGestureListener gestureListener = new GestureDetector.SimpleOnGestureListener(){
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
for (UnderlayButton button : buttons){
if(button.onClick(e.getX(), e.getY()))
break;
}
return true;
}
};
private GestureDetector.SimpleOnGestureListener gestureListener2 = new GestureDetector.SimpleOnGestureListener(){
#Override
public boolean onSingleTapConfirmed(MotionEvent e) {
for (UnderlayButton button : buttons){
if(button.onClick2(e.getX(), e.getY()))
break;
}
return true;
}
};
private View.OnTouchListener onTouchListener = new View.OnTouchListener() {
#Override
public boolean onTouch(View view, MotionEvent e) {
if (swipedPos < 0) return false;
Point point = new Point((int) e.getRawX(), (int) e.getRawY());
RecyclerView.ViewHolder swipedViewHolder = recyclerView.findViewHolderForAdapterPosition(swipedPos);
View swipedItem = swipedViewHolder.itemView;
Rect rect = new Rect();
swipedItem.getGlobalVisibleRect(rect);
if (e.getAction() == MotionEvent.ACTION_DOWN || e.getAction() == MotionEvent.ACTION_UP ||e.getAction() == MotionEvent.ACTION_MOVE) {
if (rect.top < point.y && rect.bottom > point.y) {
gestureDetector.onTouchEvent(e);
gestureDetector2.onTouchEvent(e);
}
//else if(rect.top > point.y && rect.bottom < point.y){ }
else {
recoverQueue.add(swipedPos);
swipedPos = -1;
recoverSwipedItem();
}
}
return false;
}
};
public SwipeHelper(Context context, RecyclerView recyclerView) {
super(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT);
this.recyclerView = recyclerView;
this.buttons = new ArrayList<>();
this.gestureDetector = new GestureDetector(context, gestureListener);
this.gestureDetector2 = new GestureDetector(context, gestureListener2);
this.recyclerView.setOnTouchListener(onTouchListener);
buttonsBuffer = new HashMap<>();
recoverQueue = new LinkedList<Integer>(){
#Override
public boolean add(Integer o) {
if (contains(o))
return false;
else
return super.add(o);
}
};
attachSwipe();
}
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
int pos = viewHolder.getAdapterPosition();
if (swipedPos != pos)
recoverQueue.add(swipedPos);
swipedPos = pos;
if (buttonsBuffer.containsKey(swipedPos))
buttons = buttonsBuffer.get(swipedPos);
else
buttons.clear();
buttonsBuffer.clear();
swipeThreshold = 0.5f * buttons.size() * BUTTON_WIDTH;
recoverSwipedItem();
}
#Override
public float getSwipeThreshold(RecyclerView.ViewHolder viewHolder) {
return swipeThreshold;
}
#Override
public float getSwipeEscapeVelocity(float defaultValue) {
return 0.1f * defaultValue;
}
#Override
public float getSwipeVelocityThreshold(float defaultValue) {
return 5.0f * defaultValue;
}
#Override
public void onChildDraw(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
int pos = viewHolder.getAdapterPosition();
float translationX = dX;
View itemView = viewHolder.itemView;
if (pos < 0){
swipedPos = pos;
return;
}
if(actionState == ItemTouchHelper.ACTION_STATE_SWIPE){
if(dX < 0) {
List<UnderlayButton> buffer = new ArrayList<>();
if (!buttonsBuffer.containsKey(pos)){
instantiateUnderlayButton(viewHolder, buffer);
buttonsBuffer.put(pos, buffer);
}
else {
buffer = buttonsBuffer.get(pos);
}
translationX = dX * buffer.size() * BUTTON_WIDTH / itemView.getWidth();
drawButtons(c, itemView, buffer, pos, translationX);
}
if(dX > 0) {
List<UnderlayButton> buffer = new ArrayList<>();
if (!buttonsBuffer.containsKey(pos)){
instantiateUnderlayButton(viewHolder, buffer);
buttonsBuffer.put(pos, buffer);
}
else {
buffer = buttonsBuffer.get(pos);
}
translationX = dX * buffer.size() * BUTTON_WIDTH / itemView.getWidth();
drawButtons(c, itemView, buffer, pos, translationX);
}
}
super.onChildDraw(c, recyclerView, viewHolder, translationX, dY, actionState, isCurrentlyActive);
}
private synchronized void recoverSwipedItem(){
while (!recoverQueue.isEmpty()){
int pos = recoverQueue.poll();
if (pos > -1) {
recyclerView.getAdapter().notifyItemChanged(pos);
}
}
}
private void drawButtons(Canvas c, View itemView, List<UnderlayButton> buffer, int pos, float dX) {
float right = itemView.getRight();
float left = itemView.getLeft();
float dButtonWidth = (-1) * dX / buffer.size();
for (UnderlayButton button : buffer) {
if (dX < 0) {
left = right - dButtonWidth;
button.onDraw(
c,
new RectF(
left,
itemView.getTop(),
right,
itemView.getBottom()
),
pos, dX
);
right = left;
} else if (dX > 0) {
right = left - dButtonWidth;
button.onDraw(c,
new RectF(
right,
itemView.getTop(),
left,
itemView.getBottom()
), pos, dX
);
left = right;
}
}
}
public void attachSwipe(){
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(this);
itemTouchHelper.attachToRecyclerView(recyclerView);
}
public abstract void instantiateUnderlayButton(RecyclerView.ViewHolder viewHolder, List<UnderlayButton> underlayButtons);
public static class UnderlayButton {
private String text;
private int imageResId;
private int color;
private UnderlayButtonClickListener clickListener;
private String text2;
private int imageResId2;
private int color2;
private UnderlayButtonClickListener2 clickListener2;
private RectF clickRegion;
private RectF clickRegion2;
private int pos;
public UnderlayButton(String text, int imageResId, int color, UnderlayButtonClickListener clickListener, String text2, int imageResId2, int color2, UnderlayButtonClickListener2 clickListener2) {
this.text = text;
this.imageResId = imageResId;
this.color = color;
this.clickListener = clickListener;
this.text2 = text2;
this.imageResId2 = imageResId2;
this.color2 = color2;
this.clickListener2 = clickListener2;
}
public boolean onClick(float x, float y){
if (clickRegion != null && clickRegion.contains(x, y)){
clickListener.onClick(pos);
return true;
}
return false;
}
public boolean onClick2(float x, float y){
if (clickRegion2 != null && clickRegion2.contains(x, y)){
clickListener2.onClick(pos);
return true;
}
return false;
}
public void onDraw(Canvas c, RectF rect, int pos, float dX) {
Paint p = new Paint();
// Draw background
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (dX < 0) {
//Boton derecho
c.drawRect(rect, p);
p.setColor(color);
}
else{
//Boton Izquierdo
p.setColor(color2);
c.drawRect(rect, p);
}
clickRegion = rect;
clickRegion2 = rect;
this.pos = pos;
}
}
}
public interface UnderlayButtonClickListener {
void onClick(int pos);
}
public interface UnderlayButtonClickListener2 {
void onClick(int pos);
}}
I tried adding more parameters to the "UnderlayButton" class because before I tried to use the two classes separately but it was throwing me problems with the OnClickListener.
So the best way I found was to add extra parameters in the "UnderlayButton" class. Still, it doesn't work for me.
Could someone help me please.
I have a Gifview library which plays the animated gif. It's working fine in half screen but I need the gif to play in fullscreen mode. I Want to modify the onMeasure so that it can fit the fullscreen.
How can I change this method so that fullscreen is take in scale for each device?
public class GifView extends View {
private static final int DEFAULT_MOVIE_VIEW_DURATION = 1000;
private int mMovieResourceId;
private Movie movie;
private long mMovieStart;
private int mCurrentAnimationTime;
/**
* Position for drawing animation frames in the center of the view.
*/
private float mLeft;
private float mTop;
/**
* Scaling factor to fit the animation within view bounds.
*/
private float mScale;
/**
* Scaled movie frames width and height.
*/
private int mMeasuredMovieWidth;
private int mMeasuredMovieHeight;
private volatile boolean mPaused;
private boolean mVisible = true;
public GifView(Context context) {
this(context, null);
}
public GifView(Context context, AttributeSet attrs) {
this(context, attrs, R.styleable.CustomTheme_gifViewStyle);
}
public GifView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
setViewAttributes(context, attrs, defStyle);
}
#SuppressLint("NewApi")
private void setViewAttributes(Context context, AttributeSet attrs, int defStyle) {
/**
* Starting from HONEYCOMB(Api Level:11) have to turn off HW acceleration to draw
* Movie on Canvas.
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}
final TypedArray array = context.obtainStyledAttributes(attrs,
R.styleable.GifView, defStyle, R.style.Widget_GifView);
//-1 is default value
mMovieResourceId = array.getResourceId(R.styleable.GifView_gif, -1);
mPaused = array.getBoolean(R.styleable.GifView_paused, false);
array.recycle();
if (mMovieResourceId != -1) {
movie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));
}
}
public void setGifResource(int movieResourceId) {
this.mMovieResourceId = movieResourceId;
movie = Movie.decodeStream(getResources().openRawResource(mMovieResourceId));
requestLayout();
}
public int getGifResource() {
return this.mMovieResourceId;
}
public void play() {
if (this.mPaused) {
this.mPaused = false;
/**
* Calculate new movie start time, so that it resumes from the same
* frame.
*/
mMovieStart = android.os.SystemClock.uptimeMillis() - mCurrentAnimationTime;
invalidate();
}
}
//skip
public void next(){
if (this.mPaused) {
this.mPaused = false;
mMovieStart = mCurrentAnimationTime + movie.duration();
invalidate();
}
}
public void pause() {
if (!this.mPaused) {
this.mPaused = true;
invalidate();
}
}
public boolean isPaused() {
return this.mPaused;
}
public boolean isPlaying() {
return !this.mPaused;
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (movie != null) {
int movieWidth = movie.width();
int movieHeight = movie.height();
/*
* Calculate horizontal scaling
*/
float scaleH = 1f;
int measureModeWidth = MeasureSpec.getMode(widthMeasureSpec);
if (measureModeWidth != MeasureSpec.UNSPECIFIED) {
int maximumWidth = MeasureSpec.getSize(widthMeasureSpec);
if (movieWidth > maximumWidth) {
scaleH = (float) movieWidth / (float) maximumWidth;
}
}
/*
* calculate vertical scaling
*/
float scaleW = 1f;
int measureModeHeight = MeasureSpec.getMode(heightMeasureSpec);
if (measureModeHeight != MeasureSpec.UNSPECIFIED) {
int maximumHeight = MeasureSpec.getSize(heightMeasureSpec);
if (movieHeight > maximumHeight) {
scaleW = (float) movieHeight / (float) maximumHeight;
}
}
/*
* calculate overall scale
*/
mScale = 1f / Math.max(scaleH, scaleW);
mMeasuredMovieWidth = (int) (movieWidth * mScale);
mMeasuredMovieHeight = (int) (movieHeight * mScale);
setMeasuredDimension(mMeasuredMovieWidth, mMeasuredMovieHeight);
} else {
/*
* No movie set, just set minimum available size.
*/
setMeasuredDimension(getSuggestedMinimumWidth(), getSuggestedMinimumHeight());
}
}
#Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
/*
* Calculate mLeft / mTop for drawing in center
*/
mLeft = (getWidth() - mMeasuredMovieWidth) / 2f;
mTop = (getHeight() - mMeasuredMovieHeight) / 2f;
mVisible = getVisibility() == View.VISIBLE;
}
#Override
protected void onDraw(Canvas canvas) {
if (movie != null) {
if (!mPaused) {
updateAnimationTime();
drawMovieFrame(canvas);
invalidateView();
} else {
drawMovieFrame(canvas);
}
}
}
/**
* Invalidates view only if it is mVisible.
* <br>
* {#link #postInvalidateOnAnimation()} is used for Jelly Bean and higher.
*/
#SuppressLint("NewApi")
private void invalidateView() {
if (mVisible) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
postInvalidateOnAnimation();
} else {
invalidate();
}
}
}
/**
* Calculate current animation time
*/
private void updateAnimationTime() {
long now = android.os.SystemClock.uptimeMillis();
if (mMovieStart == 0) {
mMovieStart = now;
}
int dur = movie.duration();
if (dur == 0) {
dur = DEFAULT_MOVIE_VIEW_DURATION;
}
mCurrentAnimationTime = (int) ((now - mMovieStart) % dur);
// Log.d(TAG, "updateAnimationTime: " + mCurrentAnimationTime);
}
/**
* Draw current GIF frame
*/
private void drawMovieFrame(Canvas canvas) {
movie.setTime(mCurrentAnimationTime);
// canvas.save(Canvas.MATRIX_SAVE_FLAG);
canvas.scale(mScale, mScale);
movie.draw(canvas, mLeft / mScale, mTop / mScale);
canvas.restore();
}
#SuppressLint("NewApi")
#Override
public void onScreenStateChanged(int screenState) {
super.onScreenStateChanged(screenState);
mVisible = screenState == SCREEN_STATE_ON;
invalidateView();
}
#SuppressLint("NewApi")
#Override
protected void onVisibilityChanged(View changedView, int visibility) {
super.onVisibilityChanged(changedView, visibility);
mVisible = visibility == View.VISIBLE;
invalidateView();
}
#Override
protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility);
mVisible = visibility == View.VISIBLE;
invalidateView();
}
}
xml layout
<com.world.example.GifView
android:id="#+id/gif1"
android:layout_width="match_parent"
android:layout_height="match_parent"
custom:gif="#mipmap/tiktok" />
I want the gif image to play in fullscreen mode.
Try android:scaleType="fitXY" in xml layout
<com.world.example.GifView
android:id="#+id/mygif"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scaleType="fitXY"/>
Hope it helps !
I try to make image editor application. in this application i try to put multiple sticker on image that sticker can move , zoom , rotate, and also selected sticker can erase . can anyone help me ? here is code that i done with sticker move,rotate,zoom but i cant erase selected sticker .
public class MainActivity extends AppCompatActivity {
Bitmap originalBitmap,originalBitmap1;
RelativeLayout rlImageViewContainer;
private ImageView ivRedo;
private ImageView ivUndo;
Button btn_erase;
private ArrayList<Path> paths;
private ArrayList<Path> redoPaths;
Paint destPaint = new Paint();
Path destPath = new Path();
Bitmap destBitmap;
Canvas destCanvas = new Canvas();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
originalBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.a);
originalBitmap1 = BitmapFactory.decodeResource(getResources(), R.drawable.b);
initView();
}
private void initView() {
ivUndo = (ImageView) findViewById(R.id.iv_undo);
ivRedo = (ImageView) findViewById(R.id.iv_redo);
rlImageViewContainer = findViewById(R.id.rl_image_view_container);
final PhotoSortrView photoSortrView = new PhotoSortrView(this);
rlImageViewContainer.addView(photoSortrView,0);
photoSortrView.setBackgroundColor(getResources().getColor(R.color.tran));
photoSortrView.addImages(this, originalBitmap);
photoSortrView.addImages(this, originalBitmap1);
ivUndo.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
// undo();
}
});
ivRedo.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// redo();
}
});
btn_erase = (Button) findViewById(R.id.btn_erase);
btn_erase.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (photoSortrView.isErase) {
btn_erase.setBackgroundColor(Color.RED);
photoSortrView.isErase = false;
photoSortrView.isMove = true;
} else {
btn_erase.setBackgroundColor(Color.GREEN);
photoSortrView.isErase = true;
photoSortrView.isMove = false;
}
}
});
}
}
PhotoSortrView.java
public class PhotoSortrView extends View implements MultiTouchController.MultiTouchObjectCanvas<MultiTouchEntity> {
private static final String TAG = "PhotoSortr ####### ";
// private static final int[] IMAGES = { R.drawable.m74hubble };
private Path drawPath = new Path();
// drawing and canvas paint
private Paint drawPaint = new Paint(), canvasPaint = new Paint();
// initial color
private int paintColor = Color.TRANSPARENT;
// canvas
private Canvas drawCanvas = new Canvas();
// canvas bitmap
private Bitmap canvasBitmap;
private ArrayList<MultiTouchEntity> imageIDs = new ArrayList<MultiTouchEntity>();
// --
private MultiTouchController<MultiTouchEntity> multiTouchController = new MultiTouchController<MultiTouchEntity>(this);
// --
private MultiTouchController.PointInfo currTouchPoint = new MultiTouchController.PointInfo();
private static final int UI_MODE_ROTATE = 1, UI_MODE_ANISOTROPIC_SCALE = 2;
private int mUIMode = UI_MODE_ROTATE;
// --
private static final float SCREEN_MARGIN = 100;
private int displayWidth, displayHeight;
public boolean isErase = false;
public boolean isMove = true;
Paint destPaint = new Paint();
Path destPath = new Path();
Bitmap destBitmap;
Canvas destCanvas = new Canvas();
int height,width;
// ---------------------------------------------------------------------------------------------------
public PhotoSortrView(Context context) {
this(context, null);
setupPaint();
init(context);
}
private void setupPaint() {
destPaint.setAlpha(0);
destPaint.setAntiAlias(true);
destPaint.setStyle(Paint.Style.STROKE);
destPaint.setStrokeJoin(Paint.Join.ROUND);
destPaint.setStrokeCap(Paint.Cap.ROUND);
destPaint.setStrokeWidth(20);
destPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
}
public PhotoSortrView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
setupDrawing();
}
public PhotoSortrView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
Log.e(TAG, "init: " );
Resources res = context.getResources();
setBackgroundColor(Color.TRANSPARENT);
DisplayMetrics metrics = res.getDisplayMetrics();
this.displayWidth = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? Math
.max(metrics.widthPixels, metrics.heightPixels) : Math.min(
metrics.widthPixels, metrics.heightPixels);
this.displayHeight = res.getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE ? Math
.min(metrics.widthPixels, metrics.heightPixels) : Math.max(
metrics.widthPixels, metrics.heightPixels);
}
public void addImages(Context context, Bitmap resourceId) {
Log.e(TAG, "addImages: " );
// setupDrawing();
Resources res = context.getResources();
imageIDs.add(new ImageEntity(resourceId, res));
float cx = SCREEN_MARGIN + (float)
(Math.random() * (displayWidth - 2 * SCREEN_MARGIN));
float cy = SCREEN_MARGIN + (float)
(Math.random() * (displayHeight - 2 * SCREEN_MARGIN));
imageIDs.get(imageIDs.size() - 1).load(context, cx, cy);
invalidate();
}
public void removeAllImages() {
imageIDs.removeAll(imageIDs);
invalidate();
}
public void removeImage() {
if (imageIDs.size() > 0) {
imageIDs.remove(imageIDs.size() - 1);
}
invalidate();
}
public int getCountImage() {
return imageIDs.size();
}
// ---------------------------------------------------------------------------------------------------
#Override
protected void onDraw(Canvas canvas) {
Log.e(TAG, "onDraw: " );
if (isErase){
Log.e(TAG, "onDraw: isErase" );
if (destBitmap != null){
destCanvas.drawPath(destPath,destPaint);
canvas.drawBitmap(destBitmap,0,0,null);
}
}
else if (isMove){
height = imageIDs.get(0).getHeight();
width = imageIDs.get(0).getWidth();
Log.e(TAG, "onDraw: isMove");
canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
canvas.drawPath(drawPath,drawPaint);
int n = imageIDs.size();
for (int i = 0; i < n; i++)
imageIDs.get(i).draw(canvas);
}
super.onDraw(canvas);
}
// ---------------------------------------------------------------------------------------------------
public void trackballClicked() {
mUIMode = (mUIMode + 1) % 3;
invalidate();
}
#Override
public boolean onTouchEvent(MotionEvent event) {
//collegeActivity.set
int action = event.getAction();
Log.e(TAG, "onTouchEvent: " );
float touchX = event.getX();
float touchY = event.getY();
// respond to down, move and up events
if (isErase){
Log.e(TAG, "onTouchEvent: isErase" );
switch (event.getAction()){
case MotionEvent.ACTION_DOWN:
destPath.moveTo(touchX,touchY);
break;
case MotionEvent.ACTION_MOVE:
destPath.lineTo(touchX,touchY);
break;
}
} else if (isMove) {
Log.e(TAG, "onTouchEvent: isMove" );
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
drawPath.moveTo(touchX, touchY);
break;
case MotionEvent.ACTION_MOVE:
drawPath.lineTo(touchX, touchY);
break;
case MotionEvent.ACTION_UP:
drawPath.lineTo(touchX, touchY);
drawCanvas.drawPath(drawPath, drawPaint);
drawPath.reset();
break;
default:
return false;
}
return multiTouchController.onTouchEvent(event);
}
invalidate();
return true;
}
private void setupDrawing() {
Log.e(TAG, "setupDrawing: " );
// prepare for drawing and setup paint stroke properties
drawPath = new Path();
drawPaint.setAlpha(0);
drawPaint.setColor(0);
drawPaint.setAntiAlias(true);
drawPaint.setStrokeWidth(10);
drawPaint.setStyle(Paint.Style.STROKE);
drawPaint.setStrokeJoin(Paint.Join.ROUND);
drawPaint.setStrokeCap(Paint.Cap.ROUND);
canvasPaint = new Paint(Paint.DITHER_FLAG);
}
// size assigned to view
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
if (w > 0 && h > 0) {
Log.e(TAG, "onSizeChanged: " );
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
drawCanvas = new Canvas(canvasBitmap);
}
}
// update color
public void setColor(String newColor) {
Log.e(TAG, "setColor: " );
invalidate();
paintColor = Color.parseColor(newColor);
drawPaint.setColor(paintColor);
}
public MultiTouchEntity getDraggableObjectAtPoint(MultiTouchController.PointInfo pt) {
float x = pt.getX(), y = pt.getY();
int n = imageIDs.size();
for (int i = n - 1; i >= 0; i--) {
ImageEntity im = (ImageEntity) imageIDs.get(i);
if (im.containsPoint(x, y))
return im;
}
return null;
}
public void selectObject(MultiTouchEntity img, MultiTouchController.PointInfo touchPoint) {
currTouchPoint.set(touchPoint);
if (img != null) {
// Move image to the top of the stack when selected
drawPaint.setColor(Color.TRANSPARENT);
imageIDs.remove(img);
imageIDs.add(img);
destCanvas = new Canvas();
destBitmap = Bitmap.createBitmap(width,height, Bitmap.Config.ARGB_8888);
destCanvas.setBitmap(destBitmap);
destCanvas.drawBitmap(img.getBitmap(),0,0,null);
} else {
// Called with img == null when drag stops.
}
invalidate();
}
public void getPositionAndScale(MultiTouchEntity img, MultiTouchController.PositionAndScale objPosAndScaleOut) {
// requires averaging the two scale factors)
objPosAndScaleOut.set(img.getCenterX(), img.getCenterY(),
(mUIMode & UI_MODE_ANISOTROPIC_SCALE) == 0,
(img.getScaleX() + img.getScaleY()) / 2,
(mUIMode & UI_MODE_ANISOTROPIC_SCALE) != 0, img.getScaleX(),
img.getScaleY(), (mUIMode & UI_MODE_ROTATE) != 0,
img.getAngle());
}
/**
* Set the position and scale of the dragged/stretched image.
*/
public boolean setPositionAndScale(MultiTouchEntity img,
MultiTouchController.PositionAndScale newImgPosAndScale, MultiTouchController.PointInfo touchPoint) {
currTouchPoint.set(touchPoint);
boolean ok = img.setPos(newImgPosAndScale);
if (ok)
invalidate();
return ok;
}
public boolean pointInObjectGrabArea(MultiTouchController.PointInfo pt, MultiTouchEntity img) {
return false;
}
}
I need to implement text line to show running right-to-left text news. I used this solution - https://stackoverflow.com/a/8971920, and added something like a messages stack and repeat times functionality. As result, there are java and xml parts of my project (link to Gist: https://gist.github.com/artseld/4aac2a3b75bb5a741216):
ScrollTextView.java
public class ScrollTextView extends TextView {
// scrolling feature
private Scroller mSlr;
// scroll speed
private float mScrollSpeed = 200f;
// the X offset when paused
private int mXPaused = 0;
// whether it's being paused
private boolean mPaused = true;
// messages
private LinkedList<Message> mMessages;
// current message
private String mMessage;
private int mRepeatTimes = 0;
/*
* constructor
*/
public ScrollTextView(Context context) {
this(context, null);
// customize the TextView
setSingleLine();
setEllipsize(null);
setVisibility(INVISIBLE);
}
/*
* constructor
*/
public ScrollTextView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
// customize the TextView
setSingleLine();
setEllipsize(null);
setVisibility(INVISIBLE);
}
/*
* constructor
*/
public ScrollTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// customize the TextView
setSingleLine();
setEllipsize(null);
setVisibility(INVISIBLE);
}
/**
* add message to messages pool
* #param message String
* #param repeatTimes int
*/
public void addMessage(String message, int repeatTimes) {
if (mMessages == null) {
mMessages = new LinkedList<>();
}
mMessages.add(new Message(message, repeatTimes));
if (mMessage == null) {
mMessage = message;
mRepeatTimes = repeatTimes;
}
startScroll();
}
public void addMessage(String message) {
addMessage(message, 1);
}
/**
* begin to scroll the text from the original position
*/
public void startScroll() {
if (mMessage == null)
return;
// set text
if (getText() == null || !getText().equals(mMessage)) {
setVisibility(INVISIBLE);
setText(mMessage);
}
// begin from the very right side
mXPaused = -1 * getWidth();
// assume it's paused
mPaused = true;
resumeScroll();
}
/**
* resume the scroll from the pausing point
*/
public void resumeScroll() {
if (!mPaused)
return;
// Do not know why it would not scroll sometimes
// if setHorizontallyScrolling is called in constructor.
setHorizontallyScrolling(true);
// use LinearInterpolator for steady scrolling
mSlr = new Scroller(this.getContext(), new LinearInterpolator());
setScroller(mSlr);
int scrollingLen = calculateScrollingLen();
int distance = scrollingLen - (getWidth() + mXPaused);
int duration = (int) (1000f * distance / mScrollSpeed);
setVisibility(VISIBLE);
mSlr.startScroll(mXPaused, 0, distance, 0, duration);
invalidate();
mPaused = false;
}
/**
* calculate the scrolling length of the text in pixel
*
* #return the scrolling length in pixels
*/
private int calculateScrollingLen() {
TextPaint tp = getPaint();
Rect rect = new Rect();
String strTxt = getText().toString();
tp.getTextBounds(strTxt, 0, strTxt.length(), rect);
int scrollingLen = rect.width() + getWidth();
rect = null;
return scrollingLen;
}
/**
* pause scrolling the text
*/
public void pauseScroll() {
if (null == mSlr)
return;
if (mPaused)
return;
mPaused = true;
// abortAnimation sets the current X to be the final X,
// and sets isFinished to be true
// so current position shall be saved
mXPaused = mSlr.getCurrX();
mSlr.abortAnimation();
}
#Override
/*
* override the computeScroll to restart scrolling when finished so as that
* the text is scrolled forever
*/
public void computeScroll() {
super.computeScroll();
if (null == mSlr) return;
if (mSlr.isFinished() && !mPaused) {
if (--mRepeatTimes > 0) {
startScroll();
return;
}
if (mMessages != null && !mMessages.isEmpty()) {
mMessage = mMessages.getFirst().getMessage();
mRepeatTimes = mMessages.getFirst().getRepeatTimes();
mMessages.removeFirst();
startScroll();
return;
}
mMessage = null;
mRepeatTimes = 0;
}
}
public float getScrollSpeed() {
return mScrollSpeed;
}
public void setScrollSpeed(float scrollSpeed) {
mScrollSpeed = scrollSpeed;
}
public boolean isPaused() {
return mPaused;
}
class Message {
private String mMessage;
private int mRepeatTimes = 1;
public Message(String message) {
mMessage = message;
}
public Message(String message, int repeatTimes) {
mMessage = message;
mRepeatTimes = repeatTimes;
}
public String getMessage() {
return mMessage;
}
public int getRepeatTimes() {
return mRepeatTimes;
}
}
}
MyActivity.java
public class MyActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
// ...
final ScrollTextView scrollText = (ScrollTextView) findViewById(R.id.scroll_text);
scrollText.addMessage("Message #1");
scrollText.addMessage("Message #2");
scrollText.addMessage("Message #3");
// ...
}
}
my_activity_layout.xml
<com.mydomain.myproject.ScrollTextView
android:id="#+id/scroll_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:singleLine="true" />
Almost all works good, but text appears on the left side of textview for a few milliseconds and only after that it runs from right to left as expected.
Can anybody help me? Thanks alot!
I'm currently implementing my own custom listview. I idea is that a longclick on a list item will scale the surrounding visible views to a smaller value scale and leave the the current view normal, whilst expanding a view below the current selected view. Once the user touches anywhere around the selected view, it will scale everything back to normal.
Unfortunately, there's an issue whereby the last view maybe partially hidden during the scale animation and so doesn't scale back to normal. So when you try to gain focus to the rest of the Listview by touching outside the selected list item, it remains a smaller scale.
I've tried a few approaches, from using ViewTreeObserver to storing the positions of the list items to make sure that they get scaled, but nothing has worked. Below is my custom listview:
public class ZoomListView extends ListView implements AdapterView.OnItemLongClickListener {
private static final String TAG = ZoomListView.class.getName();
private int _xPos;
private int _yPos;
private int _pointerId;
private Rect _viewBounds;
private boolean _isZoomed;
private OnItemClickListener _listner;
private OnItemFocused _onItemFocusedLis;
private int _expandingViewHeight = 0;
private int _previousFocusedViewHeight;
public interface OnItemFocused {
/**
* This interface can be used to be notified when a particular item should be disabled, or is currently not focused
* #param position
*/
public void onItemOutOfFocus(int position, boolean status_);
public View onItemFocused(View focusedView_, int listViewPosition_, long uniqueId_);
}
public ZoomListView(Context context_) {
super(context_);
init(context_);
}
public ZoomListView(Context context_, AttributeSet attrs) {
super(context_, attrs);
init(context_);
}
public ZoomListView(Context context_, AttributeSet attrs, int defStyle) {
super(context_, attrs, defStyle);
init(context_);
}
#Override
public void setOnItemClickListener(OnItemClickListener listener) {
if(!(listener == null)){
_listner = listener;
}
super.setOnItemClickListener(listener);
}
private void init(Context context_){
setOnItemLongClickListener(this);
}
public void setOnItemDisableListener(OnItemFocused listener_){
_onItemFocusedLis = listener_;
}
private void scaleChildViews(long rowId_, int itemPos_, float scale, boolean shouldEnable){
if (_isZoomed) {
getParent().requestDisallowInterceptTouchEvent(true);
}
int firstVisiblePosition = getFirstVisiblePosition();
int pos = pointToPosition(_xPos, _yPos);
int positionOrg = pos - firstVisiblePosition;
scaleAllVisibleViews(positionOrg, scale, shouldEnable);
}
private void scaleAllVisibleViews(final int clickedItemPosition_, final float scale_, final boolean shouldEnable_) {
Animation scaleAnimation;
if(_isZoomed){
scaleAnimation = getZoomAnimation(1f, 0.8f, 1f, 0.8f);
}else{
scaleAnimation = getZoomAnimation(0.8f, 1f, 0.8f, 1f);
}
int firstVisiblePosition = getFirstVisiblePosition();
int count = getChildCount();
for (int i = 0; i < count; i++) {
int pos = i;
if (_isZoomed) {
if (getAdapter().getItemId(clickedItemPosition_) != getAdapter().getItemId(pos)) {
scaleView(pos, scale_, shouldEnable_, scaleAnimation);
}else{
displayExpandingView(pos, clickedItemPosition_);
}
} else {
View view = getChildAt(pos);
View viewToShow = _onItemFocusedLis.onItemFocused(view, pos, getAdapter().getItemId(clickedItemPosition_));
if(viewToShow != null){
viewToShow.setVisibility(GONE);
}
scaleView(pos, scale_, shouldEnable_, scaleAnimation);
}
}
}
private void displayExpandingView(int position_, int clickedItemPosition_){
View view = getChildAt(position_);
if(view != null){
Log.v(TAG, "view is valid");
View viewToShow = _onItemFocusedLis.onItemFocused(view, position_, getAdapter().getItemId(clickedItemPosition_));
viewToShow.setVisibility(VISIBLE);
Animation flip = new CyclicFlipAnimation(50f);
flip.setDuration(500);
viewToShow.startAnimation(flip);
if(_expandingViewHeight <= 0){
viewToShow.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
_expandingViewHeight = viewToShow.getMeasuredHeight();
Log.v(TAG, "expanding view hieght is + " + _expandingViewHeight);
}
}
}
private Animation getZoomAnimation(float fromX_, float toX_, float fromY_, float toY_){
Animation scaleAnimation = new ScaleAnimation(
fromX_, toX_,
fromY_, toY_,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.RELATIVE_TO_SELF, 0.5f);
scaleAnimation.setFillAfter(true);
scaleAnimation.setDuration(500);
return scaleAnimation;
}
private void scaleView(int position_, float scale_, boolean shouldEnable_, Animation animation_){
View view = getChildAt(position_);
if (view != null) {
view.startAnimation(animation_);
if (_onItemFocusedLis != null) {
_onItemFocusedLis.onItemOutOfFocus(position_, shouldEnable_);
}
}
}
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
_isZoomed = true;
scaleChildViews(l, i, 0.8f, false);
return true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
_xPos = (int) event.getX();
_yPos = (int) event.getY();
_pointerId = event.getPointerId(0);
if (_isZoomed) {
if (!_viewBounds.contains(_xPos, _yPos)) {
_isZoomed = false;
scaleChildViews(1, 1, 1f, true);
}
return false;
}
int position = pointToPosition(_xPos, _yPos);
int childNum = (position != INVALID_POSITION) ? position - getFirstVisiblePosition() : -1;
View itemView = (childNum >= 0) ? getChildAt(childNum) : null;
if (itemView != null) {
_viewBounds = getChildViewRect(this, itemView);
}
break;
}
return super.onTouchEvent(event);
}
private Rect getChildViewRect(View parentView, View childView) {
final Rect childRect = new Rect(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom() + _expandingViewHeight);
if (parentView == childView) {
return childRect;
}
ViewGroup parent = (ViewGroup) childView.getParent();
while (parent != parentView) {
childRect.offset(parent.getLeft(), parent.getTop());
childView = parent;
parent = (ViewGroup) childView.getParent();
}
return childRect;
}
}
Any idea's on what maybe wrong, thanks.
To fix this issue I ended up adding a ACTION_MOVE case statement in my onTouch method. So as long as the list items were focused again, when the list performed a move i.e. scrolled the list item, the remaining list items are restored to their original size. To achieve this you need to maintain a map/collection of id's that correspond to your list items and iterate over them once the user scrolls.
Heres the full somwhat functiong implementation:
package com.sun.tweetfiltrr.zoomlistview;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.ScaleAnimation;
import android.widget.AdapterView;
import android.widget.ListAdapter;
import android.widget.ListView;
import com.sun.tweetfiltrr.animation.CyclicFlipAnimation;
import java.util.HashMap;
import java.util.Map;
/**
*/
public class ZoomListView extends ListView implements AdapterView.OnItemLongClickListener {
private static final String TAG = ZoomListView.class.getName();
private int _xPos;
private int _yPos;
private int _pointerId;
private Rect _viewBounds;
private boolean _isZoomed;
private OnItemClickListener _listner;
private OnItemFocused _onItemFocusedLis;
private int _expandingViewHeight = 0;
private int _previousFocusedViewHeight;
private OnScrollListener _onScrollListener;
private boolean _shouldPerformScrollAnimation;
final private Map<Long, PropertyHolder> _itemIDToProperty = new HashMap<Long, PropertyHolder>();
private long _currentFocusedId;
public interface OnItemFocused {
/**
* This interface can be used to be notified when a particular item should be disabled, or is currently not focused
* #param position
*/
public void onItemScaleOut(int position, View view, boolean status_);
public void onItemRestore(int position, View view, boolean status_);
public View onItemFocused(View focusedView_, int listViewPosition_, long uniqueId_);
}
public ZoomListView(Context context_) {
super(context_);
init(context_);
}
public ZoomListView(Context context_, AttributeSet attrs) {
super(context_, attrs);
init(context_);
}
public ZoomListView(Context context_, AttributeSet attrs, int defStyle) {
super(context_, attrs, defStyle);
init(context_);
}
private void init(Context context_){
setOnItemLongClickListener(this);
}
public void setOnItemDisableListener(OnItemFocused listener_){
_onItemFocusedLis = listener_;
}
private void scaleChildViews(long rowId_, int itemPos_, float scale, boolean shouldEnable){
if (_isZoomed) {
getParent().requestDisallowInterceptTouchEvent(true);
}
scaleAllVisibleViews(shouldEnable);
}
private void scaleAllVisibleViews(final boolean shouldEnable_) {
final ListAdapter adapter = getAdapter();
Animation scaleAnimation;
if(_isZoomed){
scaleAnimation = getZoomAnimation(1f, 0.6f, 1f, 0.6f);
}else{
scaleAnimation = getZoomAnimation(0.6f, 1f, 0.6f, 1f);
}
int count = getChildCount();
for (int i = 0; i < count; i++) {
applyAnimation(i, adapter, shouldEnable_, scaleAnimation);
}
}
private void applyAnimation(int position_, ListAdapter adapter_, boolean shouldEnable_, Animation animation_){
if (_isZoomed) {
if (_currentFocusedId != adapter_.getItemId(position_)) {
scaleView(position_, shouldEnable_, animation_);
}else{
displayExpandingView(position_, adapter_);
}
}else{
if(_currentFocusedId != getAdapter().getItemId(position_)){
scaleView(position_, shouldEnable_, animation_);
}else{
View view = getChildAt(position_);
View viewToShow = _onItemFocusedLis.onItemFocused(view, position_, getAdapter().getItemId(position_));
if(viewToShow != null){
viewToShow.setVisibility(GONE);
}
}
_itemIDToProperty.remove(getAdapter().getItemId(position_));
}
}
private void displayExpandingView(int position_, ListAdapter adapter_){
View view = getChildAt(position_);
if(view != null){
View viewToShow = _onItemFocusedLis.onItemFocused(view, position_, adapter_.getItemId(position_));
viewToShow.setVisibility(VISIBLE);
Animation flip = new CyclicFlipAnimation(60f);
flip.setDuration(1000);
viewToShow.startAnimation(flip);
if(_expandingViewHeight <= 0){
viewToShow.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
_expandingViewHeight = viewToShow.getMeasuredHeight();
}
}
}
private Animation getZoomAnimation(float fromX_, float toX_, float fromY_, float toY_){
Animation scaleAnimation = new ScaleAnimation(
fromX_, toX_,
fromY_, toY_,
Animation.RELATIVE_TO_SELF, 0.5f,
Animation.ABSOLUTE, 0.7f);
scaleAnimation.setFillAfter(true);
scaleAnimation.setDuration(500);
return scaleAnimation;
}
private void scaleView(int position_, boolean shouldEnable_, Animation animationScale_ ){
View view = getChildAt(position_);
ListAdapter adapter = getAdapter();
long id = adapter.getItemId(position_);
view.startAnimation(animationScale_);
PropertyHolder holder = _itemIDToProperty.get(id);
if (holder == null) {
holder = new PropertyHolder((int) view.getTop(), (int) (view.getBottom()));
_itemIDToProperty.put(id, holder);
}
int h = view.getHeight();
if (_isZoomed) {
view.animate().translationYBy((h * 0.5f)).setDuration(500).start();
if (_onItemFocusedLis != null) {
_onItemFocusedLis.onItemScaleOut(position_, view, shouldEnable_);
}
} else {
view.animate().translationYBy(-(h * 0.5f )).setDuration(500).start();
if (_onItemFocusedLis != null) {
_onItemFocusedLis.onItemRestore(position_, view, shouldEnable_);
}
}
}
#Override
public boolean onItemLongClick(AdapterView<?> adapterView, View view, int i, long l) {
_isZoomed = true;
int firstVisiblePosition = getFirstVisiblePosition();
int pos = pointToPosition(_xPos, _yPos);
int positionOrg = pos - firstVisiblePosition;
_currentFocusedId = getAdapter().getItemId(positionOrg);
scaleChildViews(l, i, 0.8f, false);
return true;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
_xPos = (int) event.getX();
_yPos = (int) event.getY();
_pointerId = event.getPointerId(0);
if (_isZoomed) {
if (!_viewBounds.contains(_xPos, _yPos)) {
_isZoomed = false;
scaleChildViews(1, 1, 1f, true);
}
return false;
}
int position = pointToPosition(_xPos, _yPos);
int childNum = (position != INVALID_POSITION) ? position - getFirstVisiblePosition() : -1;
View itemView = (childNum >= 0) ? getChildAt(childNum) : null;
if (itemView != null) {
_viewBounds = getChildViewRect(this, itemView);
}
break;
case MotionEvent.ACTION_MOVE:
if (!_isZoomed) {
animateRemaining();
}
break;
}
return super.onTouchEvent(event);
}
private void animateRemaining(){
if(!_itemIDToProperty.isEmpty()){
for(int i = 0; i < getChildCount(); i++){
long id = getAdapter().getItemId(i);
PropertyHolder n = _itemIDToProperty.get(id);
if(n != null){
if(_currentFocusedId != getAdapter().getItemId(i)){
scaleView(i, true, getZoomAnimation(0.6f, 1, 0.6f, 1f));
}else{
Log.v(TAG, "not translating for " + getAdapter().getItemId(i)+ " for id " + _currentFocusedId);
}
_itemIDToProperty.remove(id);
}
}
}
}
private Rect getChildViewRect(View parentView, View childView) {
final Rect childRect = new Rect(childView.getLeft(), childView.getTop(), childView.getRight(), childView.getBottom() + _expandingViewHeight);
if (parentView == childView) {
return childRect;
}
ViewGroup parent = (ViewGroup) childView.getParent();
while (parent != parentView) {
childRect.offset(parent.getLeft(), parent.getTop());
childView = parent;
parent = (ViewGroup) childView.getParent();
}
return childRect;
}
private class PropertyHolder{
int _top;
int _bot;
private PropertyHolder(int top_, int bot_){
_top = top_;
_bot = bot_;
}
}
}
It's far from perfect but its a starting point.