ListView项目滚动动画("UIKit动态" - 类似)

 Becky30712701 发布于 2023-01-29 10:20

我正在尝试ListView在滚动发生时为项目设置动画.更具体地说,我试图从iOS 7上的iMessage应用程序模拟滚动动画.我在网上找到了一个类似的例子:

为了澄清,我试图在用户滚动时对项目实现"流畅"运动效果,而不是在添加新项目时实现动画效果.我试图修改我的视图,我BaseAdapter已经查看了AbsListView源代码,看看我是否能以某种方式附加一个AccelerateInterpolator可以调整发送到子视图的绘制坐标的地方(如果这甚至是如何AbsListView设计).到目前为止,我一直无法取得任何进展.

有没有人对如何复制这种行为有任何想法?


有关谷歌搜索帮助的记录:这在ios上被称为"UIKit Dynamics".

如何复制消息在iOS 7中弹跳气泡

它内置于最近的iOS版本中.然而,它仍然有点难以使用.(2014)这是每个人都复制的帖子:广泛复制的文章令人惊讶的是,UIKit Dynamics仅适用于苹果的"收藏视图",而不是苹果的"桌面视图",因此所有的iOS debs都必须将内容从表视图转换为"集合视图"

每个人都在使用的库是BPXLFlowLayout作为起点,因为那个人几乎破解了复制iphone短信应用程序的感觉.事实上,如果你将它移植到Android我猜你可以使用那里的参数来获得相同的感觉.我注意到在我的android fone系列中,HTC手机在他们的UI上有这种效果.希望能帮助到你.Android摇滚!

3 个回答
  • 我花了几分钟时间来探索这个问题,看起来它可以很容易地用API 12及以上版本完成(希望我不会错过任何东西......).要获得非常基本的卡片效果,只需在适配器的getView()末尾处输入几行代码,然后再将其返回到列表中.这是整个适配器:

        public class MyAdapter extends ArrayAdapter<String>{
    
            private int mLastPosition;
    
            public MyAdapter(Context context, ArrayList<String> objects) {
                super(context, 0, objects);
            }
    
            private class ViewHolder{
                public TextView mTextView;
            }
    
            @TargetApi(Build.VERSION_CODES.HONEYCOMB_MR1)
            @Override
            public View getView(int position, View convertView, ViewGroup parent) {
    
                ViewHolder holder;
    
                if (convertView == null) {
                    holder = new ViewHolder();
                    convertView = LayoutInflater.from(getContext()).inflate(R.layout.grid_item, parent, false);
                    holder.mTextView = (TextView) convertView.findViewById(R.id.checkbox);
                    convertView.setTag(holder);
                } else {
                    holder = (ViewHolder) convertView.getTag();
                }
    
                holder.mTextView.setText(getItem(position));
    
                // This tells the view where to start based on the direction of the scroll.
                // If the last position to be loaded is <= the current position, we want
                // the views to start below their ending point (500f further down).
                // Otherwise, we start above the ending point.
                float initialTranslation = (mLastPosition <= position ? 500f : -500f);
    
                convertView.setTranslationY(initialTranslation);
                convertView.animate()
                        .setInterpolator(new DecelerateInterpolator(1.0f))
                        .translationY(0f)
                        .setDuration(300l)
                        .setListener(null);
    
                // Keep track of the last position we loaded
                mLastPosition = position;
    
                return convertView;
            }
    
    
        }
    

    请注意,我正在跟踪要加载的最后一个位置(mLastPosition),以确定是从底部向上(如果向下滚动)还是从顶部向下(如果我们向上滚动)为视图设置动画.

    很棒的是,你可以通过修改初始的convertView属性(例如convertView.setScaleX(float scale))和convertView.animate()链(例如.scaleX(float))来做更多的事情.

    在此输入图像描述

    2023-01-29 10:22 回答
  • 在返回你的convertView之前将它放在你的getView()方法中试试这个:

    Animation animationY = new TranslateAnimation(0, 0, holder.llParent.getHeight()/4, 0);
    animationY.setDuration(1000);
    Yourconvertview.startAnimation(animationY);  
    animationY = null; 
    

    llParent = RootLayout,其中包含自定义行项.

    2023-01-29 10:23 回答
  • 这个实现非常好.虽然有一些闪烁,可能是因为当适配器将新视图添加到顶部或底部时改变了索引.这可以通过观察树中的变化并在运行中移动索引来解决.

    public class ElasticListView extends GridView implements AbsListView.OnScrollListener,      View.OnTouchListener {
    
    private static int SCROLLING_UP = 1;
    private static int SCROLLING_DOWN = 2;
    
    private int mScrollState;
    private int mScrollDirection;
    private int mTouchedIndex;
    
    private View mTouchedView;
    
    private int mScrollOffset;
    private int mStartScrollOffset;
    
    private boolean mAnimate;
    
    private HashMap<View, ViewPropertyAnimator> animatedItems;
    
    
    public ElasticListView(Context context) {
        super(context);
        init();
    }
    
    public ElasticListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }
    
    public ElasticListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init();
    }
    
    private void init() {
        mScrollState = SCROLL_STATE_IDLE;
        mScrollDirection = 0;
        mStartScrollOffset = -1;
        mTouchedIndex = Integer.MAX_VALUE;
        mAnimate = true;
        animatedItems = new HashMap<>();
        this.setOnTouchListener(this);
        this.setOnScrollListener(this);
    
    }
    
    
    @Override
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (mScrollState != scrollState) {
            mScrollState = scrollState;
            mAnimate = true;
    
        }
        if (scrollState == SCROLL_STATE_IDLE) {
            mStartScrollOffset = Integer.MAX_VALUE;
            mAnimate = true;
            startAnimations();
        }
    
    }
    
    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
    
        if (mScrollState == SCROLL_STATE_TOUCH_SCROLL) {
    
            if (mStartScrollOffset == Integer.MAX_VALUE) {
                mTouchedView = getChildAt(mTouchedIndex - getPositionForView(getChildAt(0)));
                if (mTouchedView == null) return;
    
                mStartScrollOffset = mTouchedView.getTop();
            } else if (mTouchedView == null) return;
    
            mScrollOffset = mTouchedView.getTop() - mStartScrollOffset;
            int tmpScrollDirection;
            if (mScrollOffset > 0) {
    
                tmpScrollDirection = SCROLLING_UP;
    
            } else {
                tmpScrollDirection = SCROLLING_DOWN;
            }
    
            if (mScrollDirection != tmpScrollDirection) {
                startAnimations();
                mScrollDirection = tmpScrollDirection;
            }
    
    
            if (Math.abs(mScrollOffset) > 200) {
                mAnimate = false;
                startAnimations();
            }
            Log.d("test", "direction:" + (mScrollDirection == SCROLLING_UP ? "up" : "down") + ", scrollOffset:" + mScrollOffset + ", toucheId:" + mTouchedIndex + ", fvisible:" + firstVisibleItem + ", " +
                "visibleItemCount:" + visibleItemCount + ", " +
                "totalCount:" + totalItemCount);
            int indexOfLastAnimatedItem = mScrollDirection == SCROLLING_DOWN ?
                getPositionForView(getChildAt(0)) + getChildCount() :
                getPositionForView(getChildAt(0));
    
            //check for bounds
            if (indexOfLastAnimatedItem >= getChildCount()) {
                indexOfLastAnimatedItem = getChildCount() - 1;
            } else if (indexOfLastAnimatedItem < 0) {
                indexOfLastAnimatedItem = 0;
            }
    
            if (mScrollDirection == SCROLLING_DOWN) {
                setAnimationForScrollingDown(mTouchedIndex - getPositionForView(getChildAt(0)), indexOfLastAnimatedItem, firstVisibleItem);
            } else {
                setAnimationForScrollingUp(mTouchedIndex - getPositionForView(getChildAt(0)), indexOfLastAnimatedItem, firstVisibleItem);
            }
            if (Math.abs(mScrollOffset) > 200) {
                mAnimate = false;
                startAnimations();
                mTouchedView = null;
                mScrollDirection = 0;
                mStartScrollOffset = -1;
                mTouchedIndex = Integer.MAX_VALUE;
                mAnimate = true;
            }
        }
    }
    
    private void startAnimations() {
        for (ViewPropertyAnimator animator : animatedItems.values()) {
            animator.start();
        }
        animatedItems.clear();
    }
    
    private void setAnimationForScrollingDown(int indexOfTouchedChild, int indexOflastAnimatedChild, int firstVisibleIndex) {
        for (int i = indexOfTouchedChild + 1; i <= indexOflastAnimatedChild; i++) {
            View v = getChildAt(i);
            v.setTranslationY((-1f * mScrollOffset));
            if (!animatedItems.containsKey(v)) {
                animatedItems.put(v, v.animate().translationY(0).setDuration(300).setStartDelay(50 * i));
            }
    
        }
    }
    
    private void setAnimationForScrollingUp(int indexOfTouchedChild, int indexOflastAnimatedChild, int firstVisibleIndex) {
        for (int i = indexOfTouchedChild - 1; i > 0; i--) {
            View v = getChildAt(i);
    
            v.setTranslationY((-1 * mScrollOffset));
            if (!animatedItems.containsKey(v)) {
                animatedItems.put(v, v.animate().translationY(0).setDuration(300).setStartDelay(50 * (indexOfTouchedChild - i)));
            }
    
        }
    }
    
    
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getActionMasked()) {
            case MotionEvent.ACTION_DOWN:
                Rect rect = new Rect();
                int childCount = getChildCount();
                int[] listViewCoords = new int[2];
                getLocationOnScreen(listViewCoords);
                int x = (int)event.getRawX() - listViewCoords[0];
                int y = (int)event.getRawY() - listViewCoords[1];
                View child;
                for (int i = 0; i < childCount; i++) {
                    child = getChildAt(i);
                    child.getHitRect(rect);
                    if (rect.contains(x, y)) {
                        mTouchedIndex = getPositionForView(child); 
                        break;
                    }
                }
                return false;
    
        }
        return false;
    
    }
    
    }
    

    2023-01-29 10:24 回答
撰写答案
今天,你开发时遇到什么问题呢?
立即提问
热门标签
PHP1.CN | 中国最专业的PHP中文社区 | PNG素材下载 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有