`
龙哥IT
  • 浏览: 253855 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

试图滑动,超过屏幕,标题置顶

 
阅读更多

当在布局中,当上下滑动,需要某个标题一直显示的情况下:

package com.example.test;

import java.util.ArrayList;

import android.content.Context;
import android.graphics.Canvas;
import android.os.Build;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.AlphaAnimation;
import android.widget.ScrollView;

public class StickyScrollView extends ScrollView {

	/**
	 * Tag for views that should stick and have constant drawing. e.g. TextViews, ImageViews etc
	 */
	public static final String STICKY_TAG = "sticky";

	/**
	 * Flag for views that should stick and have non-constant drawing. e.g. Buttons, ProgressBars etc
	 */
	public static final String FLAG_NONCONSTANT = "-nonconstant";

	/**
	 * Flag for views that have aren't fully opaque
	 */
	public static final String FLAG_HASTRANSPARANCY = "-hastransparancy";

	private ArrayList<View> stickyViews;
	private View currentlyStickingView;
	private float stickyViewTopOffset;
	private boolean redirectTouchesToStickyView;
	private boolean clippingToPadding;
	private boolean clipToPaddingHasBeenSet;

	private final Runnable invalidateRunnable = new Runnable() {

		@Override
		public void run() {
			if(currentlyStickingView!=null){
				int l = getLeftForViewRelativeOnlyChild(currentlyStickingView);
				int t  = getBottomForViewRelativeOnlyChild(currentlyStickingView);
				int r = getRightForViewRelativeOnlyChild(currentlyStickingView);
				int b = (int) (getScrollY() + (currentlyStickingView.getHeight() + stickyViewTopOffset));
				invalidate(l,t,r,b);
			}
			postDelayed(this, 16);
		}
	};

	public StickyScrollView(Context context) {
		this(context, null);
	}

	public StickyScrollView(Context context, AttributeSet attrs) {
		this(context, attrs, android.R.attr.scrollViewStyle);
	}

	public StickyScrollView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		setup();
	}

	public void setup(){
		stickyViews = new ArrayList<View>();
	}
	
	private int getLeftForViewRelativeOnlyChild(View v){
		int left = v.getLeft();
		while(v.getParent() != getChildAt(0)){
			v = (View) v.getParent();
			left += v.getLeft();
		}
		return left;
	}
	
	private int getTopForViewRelativeOnlyChild(View v){
		int top = v.getTop();
		while(v.getParent() != getChildAt(0)){
			v = (View) v.getParent();
			top += v.getTop();
		}
		return top;
	}
	
	private int getRightForViewRelativeOnlyChild(View v){
		int right = v.getRight();
		while(v.getParent() != getChildAt(0)){
			v = (View) v.getParent();
			right += v.getRight();
		}
		return right;
	}
	
	private int getBottomForViewRelativeOnlyChild(View v){
		int bottom = v.getBottom();
		while(v.getParent() != getChildAt(0)){
			v = (View) v.getParent();
			bottom += v.getBottom();
		}
		return bottom;
	}

	@Override
	protected void onLayout(boolean changed, int l, int t, int r, int b) {
		super.onLayout(changed, l, t, r, b);
		if(!clipToPaddingHasBeenSet){
			clippingToPadding = true;
		}
		notifyHierarchyChanged();
	}

	@Override
	public void setClipToPadding(boolean clipToPadding) {
		super.setClipToPadding(clipToPadding);
		clippingToPadding  = clipToPadding;
		clipToPaddingHasBeenSet = true;
	}

	@Override
	public void addView(View child) {
		super.addView(child);
		findStickyViews(child);
	}

	@Override
	public void addView(View child, int index) {
		super.addView(child, index);
		findStickyViews(child);
	}

	@Override
	public void addView(View child, int index, android.view.ViewGroup.LayoutParams params) {
		super.addView(child, index, params);
		findStickyViews(child);
	}

	@Override
	public void addView(View child, int width, int height) {
		super.addView(child, width, height);
		findStickyViews(child);
	}

	@Override
	public void addView(View child, android.view.ViewGroup.LayoutParams params) {
		super.addView(child, params);
		findStickyViews(child);
	}

	@Override
	protected void dispatchDraw(Canvas canvas) {
		super.dispatchDraw(canvas);
		if(currentlyStickingView != null){
			canvas.save();
			canvas.translate(getPaddingLeft(), getScrollY() + stickyViewTopOffset + (clippingToPadding ? getPaddingTop() : 0));
			canvas.clipRect(0, (clippingToPadding ? -stickyViewTopOffset : 0), getWidth(), currentlyStickingView.getHeight());
			if(getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARANCY)){
				showView(currentlyStickingView);
				currentlyStickingView.draw(canvas);
				hideView(currentlyStickingView);
			}else{
				currentlyStickingView.draw(canvas);
			}
			canvas.restore();
		}
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev) {
		if(ev.getAction()==MotionEvent.ACTION_DOWN){
			redirectTouchesToStickyView = true;
		}

		if(redirectTouchesToStickyView){
			redirectTouchesToStickyView = currentlyStickingView != null;
			if(redirectTouchesToStickyView){
				redirectTouchesToStickyView = 
					ev.getY()<=(currentlyStickingView.getHeight()+stickyViewTopOffset) && 
					ev.getX() >= getLeftForViewRelativeOnlyChild(currentlyStickingView) && 
					ev.getX() <= getRightForViewRelativeOnlyChild(currentlyStickingView);
			}
		}else if(currentlyStickingView == null){
			redirectTouchesToStickyView = false;
		}
		if(redirectTouchesToStickyView){
			ev.offsetLocation(0, -1*((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView)));
		}
		return super.dispatchTouchEvent(ev);
	}

	private boolean hasNotDoneActionDown = true;

	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		if(redirectTouchesToStickyView){
			ev.offsetLocation(0, ((getScrollY() + stickyViewTopOffset) - getTopForViewRelativeOnlyChild(currentlyStickingView)));
		} 
		
		if(ev.getAction()==MotionEvent.ACTION_DOWN){
			hasNotDoneActionDown = false;
		}
		
		if(hasNotDoneActionDown){
			MotionEvent down = MotionEvent.obtain(ev);
			down.setAction(MotionEvent.ACTION_DOWN);
			super.onTouchEvent(down);
			hasNotDoneActionDown = false;
		}
		
		if(ev.getAction()==MotionEvent.ACTION_UP || ev.getAction()==MotionEvent.ACTION_CANCEL){
			hasNotDoneActionDown = true;
		}
		
		return super.onTouchEvent(ev);
	}

	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		super.onScrollChanged(l, t, oldl, oldt);
		doTheStickyThing();
	}

	private void doTheStickyThing() {
		View viewThatShouldStick = null;
		View approachingView = null;
		for(View v : stickyViews){
			int viewTop = getTopForViewRelativeOnlyChild(v) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop());
			if(viewTop<=0){
				if(viewThatShouldStick==null || viewTop>(getTopForViewRelativeOnlyChild(viewThatShouldStick) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))){
					viewThatShouldStick = v;
				}
			}else{
				if(approachingView == null || viewTop<(getTopForViewRelativeOnlyChild(approachingView) - getScrollY() + (clippingToPadding ? 0 : getPaddingTop()))){
					approachingView = v;
				}
			}
		}
		if(viewThatShouldStick!=null){
			stickyViewTopOffset = approachingView == null ? 0 : Math.min(0, getTopForViewRelativeOnlyChild(approachingView) - getScrollY()  + (clippingToPadding ? 0 : getPaddingTop()) - viewThatShouldStick.getHeight());
			if(viewThatShouldStick != currentlyStickingView){
				if(currentlyStickingView!=null){
					stopStickingCurrentlyStickingView();
				}
				startStickingView(viewThatShouldStick);
			}
		}else if(currentlyStickingView!=null){
			stopStickingCurrentlyStickingView();
		}
	}

	private void startStickingView(View viewThatShouldStick) {
		currentlyStickingView = viewThatShouldStick;
		if(getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARANCY)){
			hideView(currentlyStickingView);
		}
		if(((String)currentlyStickingView.getTag()).contains(FLAG_NONCONSTANT)){
			post(invalidateRunnable);
		}
	}

	private void stopStickingCurrentlyStickingView() {
		if(getStringTagForView(currentlyStickingView).contains(FLAG_HASTRANSPARANCY)){
			showView(currentlyStickingView);
		}
		currentlyStickingView = null;
		removeCallbacks(invalidateRunnable);
	}

	/**
	 * Notify that the sticky attribute has been added or removed from one or more views in the View hierarchy
	 */
	public void notifyStickyAttributeChanged(){
		notifyHierarchyChanged();
	}
	
	private void notifyHierarchyChanged(){
		if(currentlyStickingView!=null){
			stopStickingCurrentlyStickingView();
		}
		stickyViews.clear();
		findStickyViews(getChildAt(0));
		doTheStickyThing();
		invalidate();
	}

	private void findStickyViews(View v) {
		if(v instanceof ViewGroup){
			ViewGroup vg = (ViewGroup)v;
			for(int i = 0 ; i<vg.getChildCount() ; i++){
				String tag = getStringTagForView(vg.getChildAt(i));
				if(tag!=null && tag.contains(STICKY_TAG)){
					stickyViews.add(vg.getChildAt(i));
				}else if(vg.getChildAt(i) instanceof ViewGroup){
					findStickyViews(vg.getChildAt(i));
				}
			}
		}else{
			String tag = (String) v.getTag();
			if(tag!=null && tag.contains(STICKY_TAG)){
				stickyViews.add(v);
			}
		}
	}
	
	private String getStringTagForView(View v){
		Object tagObject = v.getTag();
		return String.valueOf(tagObject);
	}
	private void hideView(View v) {
		if(Build.VERSION.SDK_INT>=11){
//			v.setAlpha(0);
		}else{
			AlphaAnimation anim = new AlphaAnimation(1, 0);
			anim.setDuration(0);
			anim.setFillAfter(true);
			v.startAnimation(anim);
		}
	}

	private void showView(View v) {
		if(Build.VERSION.SDK_INT>=11){
//			v.setAlpha(1);
		}else{
			AlphaAnimation anim = new AlphaAnimation(0, 1);
			anim.setDuration(0);
			anim.setFillAfter(true);
			v.startAnimation(anim);
		}
	}

}

 然后在布局中,把滑动放进去

<com.example.test.StickyScrollView android:layout_width="fill_parent" android:layout_height="wrap_content" xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

   <FrameLayout 
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical" >
    
<ImageView  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"   
        android:scaleType="center"  
        android:src="@drawable/ic_launcher" />  
  
    <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" />  
  
</FrameLayout>
<RelativeLayout  
     android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:background="#ffffffff"
            android:padding="5.0dip"
            android:tag="sticky-nonconstant-hastransparancy"
             >
    
     <TextView
                android:id="@+id/price"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:gravity="center_vertical"
                android:text="1111111111111111111111111111111"
                android:textSize="30.0dip" />
    
</RelativeLayout>
	<LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical">
	     <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
         <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
         <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
         <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
         <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
        
          <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
         <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
         <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
         <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
         <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
         <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
         <TextView  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:layout_marginBottom="20dip"  
        android:layout_gravity="center_horizontal|bottom"  
        android:padding="12dip"  
        android:background="#AA000000"  
        android:textColor="#ffffffff"  
  
        android:text="Golden Gate" /> 
	</LinearLayout>
   <!--  <ViewStub
        android:id="@+id/stub_import"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout="@layout/title" /> -->

</LinearLayout>
</com.example.test.StickyScrollView>

 

分享到:
评论

相关推荐

    Android-滑动listview标题置顶listview吸顶效果

    本知识点主要探讨如何实现“滑动ListView时标题置顶”和“ListView吸顶效果”,以及如何使安卓系统状态栏透明化。这些功能可以为用户带来更流畅、更具吸引力的交互体验。 首先,滑动ListView时标题置顶的效果通常被...

    listView 滑动置顶漂浮

    在开发中,有时我们需要实现一个功能,当用户上滑ListView时,某些特定的条目能够“漂浮”在顶部,即使列表继续滚动,这些条目依然保持可见,这就是所谓的"滑动置顶漂浮"效果。这个功能在很多应用中都有所应用,例如...

    探索Android 滑动置顶标题的实现

    在Android开发中,滑动置顶标题是一种常见的交互设计,它通常用于新闻列表、论坛帖子等场景,使得用户可以方便地查看和切换不同主题的内容。本文将深入探讨如何实现这样的功能,主要涉及Android布局设计、触摸事件...

    一个android桌面滑动切换屏幕的控件(二)

    【标题】:“一个Android桌面滑动切换屏幕的控件(二)” 在Android开发中,为用户提供流畅且直观的界面交互至关重要,而滑动切换屏幕是实现这一目标的关键技术之一。这个博客“一个Android桌面滑动切换屏幕的控件(二...

    android ScrollView滑动置顶

    在许多应用中,我们可能需要实现一个功能,当ScrollView滚动到顶部时,某个视图(比如一个标题或者搜索栏)固定在屏幕顶部,这就是所谓的"滑动置顶"效果。这个效果通常用于提供更好的用户体验,使用户在滚动内容时...

    auto.js自动滑动屏幕

    自动滑动屏幕功能,可以自动滑动微视、抖音等。可以解放双手,让程序自己跑起来。滑动时间也是随机的,模拟人看时间时间。

    Android-滑动屏幕调整屏幕亮度.zip

    Android-滑动屏幕调整屏幕亮度.zip Android-滑动屏幕调整屏幕亮度.zip Android-滑动屏幕调整屏幕亮度.zip Android-滑动屏幕调整屏幕亮度.zip Android-滑动屏幕调整屏幕亮度.zip

    左右滑动切换屏幕,上下滑动滚屏

    在Android开发中,实现“左右滑动切换屏幕,上下滑动滚屏”的功能是一项常见的需求,这涉及到Android的触摸事件处理、布局管理以及视图滚动技术。以下将详细阐述这一主题。 1. 触摸事件处理 Android系统通过...

    android滑动标题demo

    在Android开发中,滑动标题(Sliding Title)是一种常见的用户界面设计,它提供了一种动态展示和隐藏标题的方式,增强了用户体验。这个“android滑动标题demo”是为开发者提供了一个实现滑动标题功能的实例,它实现...

    滑动屏幕切换界面显示

    在Android应用开发中,滑动屏幕切换界面显示是一种常见的交互方式,这通常涉及到视图的切换和管理。在这个场景中,我们使用了Fragment这一核心组件来处理界面的展示。Fragment可以理解为一个轻量级的Activity,可以...

    标题栏随着滑动渐变

    这种效果主要体现在用户上下滑动屏幕时,标题栏的颜色或透明度会逐渐变化,为用户带来更生动、自然的浏览体验。在Android和iOS平台上,这种效果可以通过编程实现。 在Android开发中,我们可以使用`...

    防QQListView滑动置顶删除功能

    在Android开发中,"防QQListView滑动置顶删除功能"是一种常见的用户界面交互设计,主要应用于消息列表或社交应用中。QQListView是基于Android原生ListView进行优化的一种视图组件,它提供了更加丰富的功能,如滑动...

    recyclerview 横向纵向滑动,滑动置顶,跳转到某一项item

    本教程将深入讲解如何在RecyclerView中实现横向和纵向滑动,并实现滑动置顶以及跳转到特定项的功能。 首先,我们需要理解RecyclerView的基本结构。它包含一个Adapter,用于填充数据到视图,和一个LayoutManager,...

    Android 首页滑动渐变隐藏标题栏效果.zip

    当内容超过屏幕大小时,用户可以通过滑动查看所有内容。在这个案例中,当ScrollView滚动时,标题栏的渐变隐藏效果会被触发。 - **XListView**:XListView是基于ListView的一个扩展,除了基本的列表滚动功能外,还...

    向上滑动隐藏标题栏,向下滑动显示标题栏

    在Android开发中,实现"向上滑动隐藏标题栏,向下滑动显示标题栏"的效果是一项常见的用户界面交互设计,尤其适用于手机应用中,以优化用户体验并最大化屏幕空间。这个功能通常被称为滑动手势控制的头部布局,使得...

    android ListView向上滑动隐藏标题,下拉显示标题栏,完美解决滑动出现的空白问题。

    然而,在实际应用中,我们经常遇到标题栏在用户滚动列表时的交互需求,比如标题随着ListView的滑动而隐藏或显示,以优化用户体验并提高界面的空间利用率。本教程将详细讲解如何实现这个功能,同时解决滑动过程中可能...

    滑动屏幕调整屏幕亮度-滑动屏幕调整屏幕亮度

    滑动屏幕调整屏幕亮度

    史上在简单滑动屏幕左侧返回

    在移动应用设计中,"滑动屏幕左侧返回"是一种常见的交互模式,特别是在Android和iOS平台上的应用程序。这个功能使得用户可以通过在屏幕上从左向右滑动来触发返回操作,简化了用户界面,提高了操作效率。以下是对这一...

    ListView 标题置顶实现

    标题置顶是指当用户滚动ListView时,某个特定的标题项(通常对应一个数据分组)会固定在屏幕顶部,即使在列表滚动过程中,这个标题依然可见,直到下一个标题出现并将其替换。这使得用户在浏览长列表时能清晰地识别...

    Android仿ios微信滑动删除、置顶的实现

    Android仿ios微信滑动删除、置顶的实现: 左划ListView的item,当前条目出现“删除”和“置顶”两个按钮,点击其他位置,按钮隐藏,点击“删除”,当前条目消失。点击“置顶”,当前条目显示在第一行

Global site tag (gtag.js) - Google Analytics