I have a RecyclerView and implemented drag and drop functionality using the ItemTouchHelper and ItemTouchHelper.Callback.
In ItemTouchHelper.Callback I have the callback method onMove() which gets called after the dragged view was dragged over the target view and the swap is completed.
What I am trying to implement (or find) is a new callback that is called when the dragged view is over the target view, before the swap is made. As you can see in the GIF below.
Any ideas how I can achieve this?
Thanks!
I found a solution for the question that I asked so I'm gonna post it here.
ItemTouchHelper.Callback has a method called choseDropTarget() which selects a drop target from the list of ViewHolders that are under the dragged view and it is called multiple times while dragging the view.
Here we have information to calculate when the dragged view is hovering the view below and when the hovering stops.
#Override
public RecyclerView.ViewHolder chooseDropTarget(RecyclerView.ViewHolder selected, List<RecyclerView.ViewHolder> dropTargets, int curX, int curY) {
for (RecyclerView.ViewHolder target : dropTargets) {
int top = curY - selected.itemView.getTop();
int bottom = curY + selected.itemView.getHeight();
int diff;
if (top < 0) {
diff = target.itemView.getTop() - curY;
} else {
diff = target.itemView.getBottom() - bottom;
}
if (Math.abs(diff) <= 100) {
adapter.onItemHover();
} else {
adapter.onItemHoverFinish();
}
}
return super.chooseDropTarget(selected, dropTargets, curX, curY);
}
The method calculates the offset difference between the dragged view and the bottom view and if the difference is less than 100 onItemHover() callback is called and onItemHoverFinished() otherwise.
Let me know if you have a more elegant approach for this issue. Thanks!
Here before Swap you can put your Dialog or condition and on success you can swap the positions.
#Override
public boolean onItemMove(int fromPosition, int toPosition) {
if (fromPosition < toPosition) {
for (int i = fromPosition; i < toPosition; i++) {
Collections.swap(mItems, i, i + 1);
}
} else {
for (int i = fromPosition; i > toPosition; i--) {
Collections.swap(mItems, i, i - 1);
}
}
notifyItemMoved(fromPosition, toPosition);
return true;
}
I havnt tested yet but hopefully this will help
Related
this is the code I have.It works fine for Horizontal transformation animation. I want to transform my ViewPager transition animation vertically.
public class AccordionPageTransformer implements ViewPager.PageTransformer {
#Override
public void transformPage(View page, float position) {
// Counteract the default slide transition
page.setTranslationX(-position * page.getWidth());
page.setPivotX(position < 0 ? 0 : page.getWidth());
page.setScaleX(1 - Math.abs(position));
}}
Try this for ViewPager,
if (position < -1) {
page.setAlpha(0);
} else if (position <= 1) {
page.setAlpha(1);
// Counteract the default slide transition
page.setTranslationX(page.getWidth() * -position);
//Set Y position to swipe in from top
float yPosition = position * page.getHeight();
page.setTranslationY(yPosition);
} else {
page.setAlpha(0);
}
Or you can also use ViewPager2 It provides single line code to make scrolling vertical,
myViewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
Greetings one and all,
So I am using onDraw in a custom View class to draw shapes on a RelativeLayout + TableLayout, all that works fine.
Inside the MotionEvent's ACTION_MOVE I pass the X & Y to my CustomView and call a method that returns a unique ID value for each set of coordinates / shape respectively.
Due to dragging my finger from one Shape to another: multiple values will be returned to the Console as expected and I would like to store these values in an Arrayfor future use. The issue I am having is that when I try to store the values, it only stores one value in a case where the console shows 4.
Console output looks like:
06-22 07:28:56.173 zar.myapp I/System.out: value: 354
06-22 07:28:56.193 zar.myapp I/System.out: value: 858
06-22 07:28:56.213 zar.myapp I/System.out: value: 989
06-22 07:28:56.213 zar.myapp I/System.out: value: 789
Code:
ArrayList XYcoords = new ArrayList();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_MOVE:
///Single value for each finger movement.
DotView endView = getChildForTouch((TableLayout) v, x, y);
XYcoords.add(endView.getId() );
break;
case MotionEvent.ACTION_UP:
///I tried adding them from here as well.
///When I check the size of `XYcoords` it allways return 1
System.out.println("Array Size: " + XYcoords.size());
break;
DotView class: in my MainActivity where I construct my table grid I call: Dotview.setId(i); to assign a unique ID to each cell (verified). (I only shared the relevant codes):
private static class DotView extends View {
private static final int DEFAULT_SIZE = 100;
private Paint mPaint = new Paint();
private Rect mBorderRect = new Rect();
private Paint mCirclePaint = new Paint();
private int mRadius = DEFAULT_SIZE / 4;
int id;
public DotView(Context context) {
super(context);
mPaint.setStrokeWidth(2.0f);
mPaint.setStyle(Style.STROKE);
mPaint.setColor(Color.RED);
mCirclePaint.setColor(Color.CYAN);
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawColor(Color.parseColor("#0099cc"));
mBorderRect.left = 0;
mBorderRect.top = 0;
mBorderRect.right = getMeasuredWidth();
mBorderRect.bottom = getMeasuredHeight();
canvas.drawRect(mBorderRect, mPaint);
canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredHeight() / 2,
mRadius, mCirclePaint);
}
public void setId(Int id){
this.id = id;
}
public int getId()
{
return this.id;
}
}
getChildForTouch
private DotView getChildForTouch(TableLayout table, float x, float y) {
final int childWidth = ((TableRow) table.getChildAt(0))
.getChildAt(0).getWidth();
final int childHeight = ((TableRow) table.getChildAt(0))
.getChildAt(0).getHeight();
// find out the row of the child
int row = 0;
do {
if (y > childHeight) {
row++;
y -= childHeight;
} else {
break;
}
} while (y > childHeight);
int column = 0;
do {
if (x > childWidth) {
column++;
x -= childWidth;
} else {
break;
}
} while (x > childWidth);
return (DotView) ((TableRow) table.getChildAt(row))
.getChildAt(column);
}
More code can be supplied on request.
Thanks in advance
The issue I am having is that when I try to store the values, it only
stores one value in a case where the console shows 4
I'm assuming you're creating the XYcoords list inside the onTouch() callback, in which case it will have a size of 1 corresponding to the current touch event. As the user moves his finger onTouch() will be called again with another touch event, at this point you'll create a new XYcoords list with one entry and so on...
To solve this, move the XYcoords list initialization outside of the onTouch() callback, like a field in the class. This way you'll not recreate it every time a touch event happens.
As a side note, I'm assuming that with your current code you're trying to store in a list the DotViews the user has touched as he moved his finger. In this case you'll most likely not want to simply do XYcoords.add(endView.getId()) in MotionEvent.ACTION_MOVE as you'll end up with hundreds of duplicate entries(as there will be a lot of touch events that will be triggered inside a single DotView). Instead don't allow duplicates:
if (XYcoords.size() != 0) {
// check to see if we aren't trying to add the same DotView reference
// (meaning we are still inside the same DotView)
int lastId = XYcoords.get(XYcoords.size() - 1);
if (lastId != endView.getId()) {
// not the same DotView so add it
XYcoords.add(endView.getId());
}
} else {
XYcoords.add(endView.getId()); // first event, must add it
}
In the case for MotionEvent.ACTION_UP you need to use(or store somewhere else the info in XYcoords) the data in XYcoords and clear it so next time the user starts touching the DotViews you have a fresh history(no items in XYcoords).
i started making a game that named GO at android (traditional japanese game).
refferer : GO
and i want to make a condition in each player gets a turn, so when the player get turn, the condition goes to see the position of the stone, if the stone position that placed it possible to obtain liberty, then run the conditions, otherwise not. here is the coding :
public void checkCondition(int position, View v){
final ImageButton[] arrPlayer1 = { //Board
g1 ,g2, g3, g4, g5, g6, g7, g8, g9,
g10, g11, g12, g13, g14, g15, g16, g17, g18,
g19, g20, g21, g22, g23, g24, g25, g26, g27,
g28, g29, g30, g31, g32, g33, g34, g35, g36,
g37, g38, g39, g40, g41, g42, g43, g44, g45,
g46, g47, g48, g49, g50, g51, g52, g53, g54,
g55, g56, g57, g58, g59, g60, g61, g62, g63,
g64, g65, g66, g67, g68, g69, g70, g71, g72,
g73, g74, g75, g76, g77, g78, g79, g80, g81};
int posT = position - 9; //Top Position
int posD = position + 9; //Down Position
int posL = position - 1; //Left Position
int posR = position + 1; //Right Position
ImageView BlackStone = (ImageView) findViewById(R.drawable.BlackStone);
if(v == arrPlayer1[position] && arrPlayer1[posT] != null){
if(arrPlayer1[posT] == BlackStone){
if(v == arrPlayer1[position] && arrPlayer1[posD] != null){
if(arrPlayer1[posD] == BlackStone){
if(v == arrPlayer1[position] && arrPlayer1[posR] != null){
if(arrPlayer1[posR] == BlackStone){
if(v == arrPlayer1[position] && arrPlayer1[posL] != null){
if(arrPlayer1[posL] == BlackStone){
ChangeTurn(v);
}else{
CheckLiberty(position, v);
}
}
}else{
CheckLiberty(position, v);
}
}
}else{
CheckLiberty(position, v);
}
}
}else{
CheckLiberty(position, v);
}
}
}
My logic is, I make a dynamic condition in this coding so wherever the player went turn, it would see the condition of the stone, ex : if player placed stone at G20, so the logic will see in G11, G19, G21, G29 or etc interconnected stone.
int posT = position - 9; //Top Position
int posD = position + 9; //Down Position
int posL = position - 1; //Left Position
int posR = position + 1; //Right Position
but it still error and the logcat give me error array out of bounds exception at the method (CheckCondition). so, what should I do ? any help is appreciated
First Change:
ImageView BlackStone = (ImageView) findViewById(R.drawable.BlackStone);
To
ImageView BlackStone = (ImageView) findViewById(R.id.IdOfImageview);
You need to find view of any component by its Id not its background as you are using findViewById.
I'm trying to move sprites to stop the frames in center(or move them to certain x position) when right or left pressed on screen. There are 3 sprites created using box.java in the view, placed one after another with padding, stored in arraylist.
The problem: No smooth movement and doesn't stop in the center of each frames after movement has begun, sometimes all boxes are moving on top of each others, padding is totally lost. Please let me know what I'm doing wrong, thanks a lot!
//BEGINING OF BOX.JAVA >> The problem is in this class!
//This goes in Update();
private void boxMove()
{
int get_moved_pos = getMovedPos(); //get moved pos
int sprite_size = view.getSpriteSize(); //get sprite arraylist size
currentDirection = view.getDirection(); //get direction "left" or "right" from view
if(currentDirection == "right" && isMoving == false)
{
setSpriteMovedNext();
}else
if(currentDirection == "left" && isMoving == false)
{
setSpriteMovedPrev();
}
if(currentDirection != lastDirection)
{
lastDirection = currentDirection;
//MOVE RIGHT
if(currentDirection == "right" && get_moved_pos > 0) //move left and make sure that moved pos isn't overlapping / or moving to empty space
{
//Animate left until it reaches the new x position
if(x > get_new_pos_left)
{
x -= pSpeedX;
}
Log.d("RIGHT","POS: " + get_moved_pos);
}else
//MOVE LEFT
if(currentDirection == "left" && get_moved_pos < sprite_size-1) //move left and make sure that moved pos isn't overlapping / or moving to empty space
{
//Animate right until it reaches the new x position
if(x < get_new_pos_right)
{
x += pSpeedX;
}
}
}
}
//Call when screen is touched (in View.java), to set a new position to move to.
public void resetMoving()
{
isMoving = false;
this.lastDirection = "";
Log.d("RESET", "MOVING RESET");
}
public int getMovedPos()
{
return this.smoved_pos;
}
private void setSpriteMovedNext()
{
int get_max_moved = getMovedPos();
int s_size = view.getSpriteSize();
if (isMoving == false) //take a break between movements
{
if(get_max_moved < s_size-1)
{
Log.d("NEXT", "CALLED");
this.get_new_pos_right = x + view.getNextPosX(); //current x and next stop position
this.smoved_pos += 1;
this.isMoving = true; //set to avoid double touch
Log.d("NEXT", "X POS SET: " + get_max_moved);
}
}
}
private void setSpriteMovedPrev()
{
int get_max_moved = getMovedPos();
if (isMoving == false) //take a break between movements
{
if(get_max_moved > 0)
{
Log.d("PREV", "CALLED");
this.get_new_pos_left = x - view.getNextPosX(); //get current x pos and prev stop position
this.smoved_pos -= 1; //to limit the movements
this.isMoving = true; //set to avoid double touch
Log.d("PREV", "X POS SET: " + get_max_moved);
}
}
}
//END OF BOX.JAVA
//VIEW
//Add boxes
public void addBox()
{
int TOTAL_BOXES = 3;
int padding_left = 200;
int padding_tmp = this.getWidth()/2;
box.clear(); //clear old
//Box 1
box.add(new Boxes(box, this, "box1",
padding_tmp,
this.getHeight()/2,
boxSpriteImage, 1, 2, 0, 0));
padding_tmp += boxSpriteImage.getWidth()/TOTAL_BOXES + padding_left;
//Box 2
box.add(new Boxes(box, this, "box2",
padding_tmp,
this.getHeight()/2,
boxSpriteImage, 1, 2, 1, 1));
padding_tmp += boxSpriteImage.getWidth()/TOTAL_BOXES + padding_left;
//Box 3
box.add(new Boxes(box, this, "box3",
padding_tmp,
this.getHeight()/2,
boxSpriteImage, 1, 2, 2, 1));
}
public boolean onTouchEvent(MotionEvent event){
if (System.currentTimeMillis() - lastClick > 100){
lastClick = System.currentTimeMillis();
float x = event.getX();
float y = event.getY();
synchronized (getHolder())
{
if(isBoxWindow() == true)
{
if(x >= this.getWidth()/2)
{
Direction = "right";
}else
{
Direction = "left";
}
}
}
}
//called in box.java to get next x pos to move
public float getNextPosX()
{
int PADDING = 200; //padding between frames
next_pos_x = boxSprite.getWidth()/TOTAL_COLUMNS + PADDING;
return next_pos_x;
}
I think your error is in the if statements, where you compare currentDirection and lastDirection (I'm assuming that lastDirection is a String) with other Strings using the == operator. The == almost operator never works when you want to compare Objects for equality. You should use the equals() method.
For eg.
if(currentDirection != lastDirection)
should be written as:
if(!currentDirection.equals(lastDirection)
Make such changes in your code(They are needed at many places!) and I think your problem should be solved.
A good debugging practice would be logging data about your app, from each of the if blocks, to see if each of them is executed. You could have found out if your if statements are being executed.
EDIT: Why have you put this code?
if (System.currentTimeMillis() - lastClick > 100)
This means onTouchEvents are only interpreted after 100ms. remove it and check, probably that's what is causing the problem.
Alrite, decided to use onFling() method and call via View instead of adding the animations separately into the class itself, works really well when called box.get(i).update() in a loop of all added boxes, all of them animated equally. Thanks udiboy.
I'm trying to create a word jumble game where you drag a letter right or left in the jumbled word and the letters swap. What is the best way to reorder items in a RelativeLayout programmatically so when the letter is dragged left the letters that the tile passes are positioned to the right of the dragged letter.
I've tried something like this as a basic test.
public static void moveTile(Tile tile, int x, RelativeLayout parent) {
if (x < tile.getWidth()) {
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.LEFT_OF, tile.getId() - 1);
tile.setLayoutParams(params);
RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
p.addRule(RelativeLayout.RIGHT_OF, tile.getId());
Tile t = (Tile) parent.findViewById(tile.getId() - 1);
t.setLayoutParams(p);
}
parent.invalidate();
}
But this causes the app to crash with an error about "Circular dependancies cannot exist in a RelativeLayout" which I understand but I'm just not sure what other way to do this.
Any help will be much appreciated.
Yes, this error means that you must not have an object1 be toRightOf object2 and object2 toLeftOf object 1
I think in your case you should not use toRightOf and toLeftOf to position your views but try to use just margin left and margin top setting for all of them. In this way your child views would be independed of each other and you can move them around by changing their margins.
I ended up using a LinearLayout and just removing the tile before or after and replacing it with the currently selected tile something like this.
public static void moveTile(Tile tile, int x, LinearLayout parent) {
if (x < 0) {
int t = Math.abs(x) / tile.getWidth() + 1;
if (tile.getId() - t >= 0) {
Tile new_tile = (Tile) parent.findViewById(tile.getId() - t);
parent.removeViewAt(new_tile.getId());
parent.addView(new_tile, tile.getId());
int id = tile.getId();
tile.setId(new_tile.getId());
new_tile.setId(id);
}
} else if (x > tile.getWidth()) {
int t = x / tile.getWidth();
if (tile.getId() + t < word.length()) {
Tile new_tile = (Tile) parent.findViewById(tile.getId() + t);
parent.removeViewAt(new_tile.getId());
parent.addView(new_tile, tile.getId());
int id = tile.getId();
tile.setId(new_tile.getId());
new_tile.setId(id);
}
}
}