Monday, January 21, 2013

Planning: Flying cells

Planning: Flying cells
The last article in the series on customizing ListView. This time, animating the change of the selected item. In this article, we will develop a flying cell.



Practice

To animate flying cells need to change the layout main.xml, which is our ListView. Add the ImageView, which will act as a flying cell. We will use this idea on because animate View the item itself, as we did in the previous article, we did not succeed. Because each cell of the list Is located under each other, and when you move down the upper cells, they will overlap the bottom cell. 
Исходный код (main.xml)
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/white" >
    <ListView
        android:id="@+id/listview"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:cacheColorHint="@android:color/transparent" />
    <ImageView
        android:id="@+id/flyingCell"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:visibility="gone" />
</FrameLayout>
Add the necessary attributes in Activity.
// Messages for Handler
public static final int MSG_ANIMATION_FLY   = 3;
// Stores the size of the screen
private DisplayMetrics dm = new DisplayMetrics();
// Flying cell
private ImageView flying_cell;

In the method onCreate() add initialization ImageView and DisplayMetrics.
flying_cell = (ImageView)findViewById(R.id.flyingCell);
getWindowManager().getDefaultDisplay().getMetrics(dm);

Develop a method in our Activity, that will perform the movement of the selected item from its current position to a specified.
  /**
  * Starting animation flying cell
  */
 private void startFly(Bitmap cellBitmap, Point start, Point end, ToDoItem item)
 {

  flying_cell.setVisibility(View.VISIBLE);
  flying_cell.setImageBitmap(cellBitmap);
        
  TranslateAnimation translateAnimation = new TranslateAnimation(start.x, end.x, start.y, end.y);
  translateAnimation.setStartOffset(100);
  translateAnimation.setDuration(700);
  translateAnimation.setInterpolator(AnimationUtils.loadInterpolator(this,
                    android.R.anim.accelerate_decelerate_interpolator));

  /* 
   This uses AnimationSet, so you can add some moreanimation 
   (for example, first to zoom in the cell, and then move to reduce normal size)
   */
  AnimationSet spriteAnimation = new AnimationSet(true);
        spriteAnimation.addAnimation(translateAnimation);
        spriteAnimation.setAnimationListener(new FlyAnimationListenter());
  
  flying_cell.startAnimation(spriteAnimation);
 }

Our task is to keep track of the ending animation, and then update the list.
  /**
  * Listenter used to update the list after the cell has arrived to the desired position
  */
 public class FlyAnimationListenter implements Animation.AnimationListener
 {
  
   @Override
  public void onAnimationEnd(Animation arg0) 
  { 
    getHandler().sendEmptyMessage(MSG_CHANGE_ITEM);     
  }

  @Override
  public void onAnimationRepeat(Animation animation) {

    
  }

  @Override
  public void onAnimationStart(Animation animation) {

  } 
 }

As well add a method that allows you to determine whether the animation is running or not. This is to ensure that same time can be done only one animation.
  /**
  * Animation is running or not
  */
 public boolean isAnimationStarted()
 {
  return flying_cell.isShown();
   
 }

В In Handler add additional case. The idea of ​​flying cells is the following algorithm.When you select a cell, we obtain its image (Bitmap). Next are counting the initial position of the cell as the top point of list + height of the first cell + total height of all the cells that go to the selected cell. Then you sort the list and calculate the same formula the final position. After correct the end point with the size of the screen and execute the animation.
 ...
 case MSG_CHANGE_ITEM: // Animation is complete of flying cells
    flying_cell.setVisibility(View.GONE);
    adapter.notifyDataSetChanged();
    setCountPurchaseProduct();
    break; 
 ...
 case MSG_ANIMATION_FLY: // Start animation flying cells
    Bitmap cellBitmap = (Bitmap)msg.obj;
    ToDoItem item = list.get(msg.arg1);
    item.setCheck(!item.isCheck());
    Point startPoint, endPoint;    
    
    /*
     We need the following values to find the position:
     height = Height of one cell
     header_height = Height Header's list  (just the height of the first cell in the list)
     listTop = highest point of the list - the size of scroll area
     */
    int height = listview.getChildAt(1).getHeight();  
    int header_height = listview.getChildAt(0).getBottom();
    int listTop = listview.getTop() - height * listview.getFirstVisiblePosition();
    
    /*
     Searching for the starting and the end point is the following algorithm:
     1. The starting point: the top point of list + height of the first cell + total height of all the cells that go to the selected cell
     2. Sort the list according to the new position of the selected cell
     3. Find the end point: high point list + height of the first cell + total height of all the cells that go to the selected cell
     */
    startPoint = new Point(0, listTop + header_height + height * getPositionInList(item.getIndex()));
    Utils.sorting(list, 0);
    saveList();
    endPoint = new Point(0, listTop + header_height + height * getPositionInList(item.getIndex())); 
    
    // Correct the end point with the size of the screen
    if (endPoint.y > dm.heightPixels)
     endPoint.y = dm.heightPixels;
    else if (endPoint.y < -1*height)
     endPoint.y = -1*height;
    
    // If the cell changes position, the start animation
    if (endPoint.y != startPoint.y)
     startFly(cellBitmap, startPoint, endPoint, item);
    else 
     handler.sendEmptyMessage(MSG_CHANGE_ITEM);
    break;

Now you need to change all the methods that are used to select the item in accordance with the new changes:
  • method onItemClickListener() for Listview:
 listview.setOnItemClickListener(new OnItemClickListener() {

   @Override
   public void onItemClick(AdapterView parent, View view, int position,
     long id) {  
    // If there is a tap on the Header or Footer or animation is running, do not do anything
    if (position == 0 || position == list.size() + 1 || isAnimationStarted())
     return;
    Message msg = new Message();
    msg.arg1 = position - 1;
    // If was detected swipe we delete an item
    if (swipeDetector.swipeDetected()){
                    if (swipeDetector.getAction() == SwipeDetector.Action.LR || 
                      swipeDetector.getAction() == SwipeDetector.Action.RL)
                    {
                     msg.what = MSG_ANIMATION_REMOVE;
                     msg.arg2 = swipeDetector.getAction() == SwipeDetector.Action.LR ? 1 : 0;
                     msg.obj = view;
                    }
                } 
    // Otherwise, select an item
    else           
                {
                 view.setDrawingCacheEnabled(true);              
              Bitmap cellBitmap = view.getDrawingCache().copy(Config.ARGB_8888, false);
              view.setVisibility(View.INVISIBLE);
              msg.obj = cellBitmap;
     msg.what = MSG_ANIMATION_FLY;
                }

    handler.sendMessage(msg);
   }
  });
  • method OnClickListener() for CheckBox in the class CustomListAdapter:
final View tmp = v;

listitem_check.setOnClickListener(new OnClickListener() {
   
   @Override
   public void onClick(View v) {
    if (((MainActivity)context).isAnimationStarted())
     return;
    tmp.setDrawingCacheEnabled(true);
          
          Bitmap cellBitmap = tmp.getDrawingCache().copy(Config.ARGB_8888, false);
          tmp.setVisibility(View.INVISIBLE);
          Message msg = new Message();
          msg.obj = cellBitmap;
          msg.arg1 = position;
    msg.what = MainActivity.MSG_ANIMATION_FLY;
    ((MainActivity)context).getHandler().sendMessage(msg);
    
   }
  });

Links