如何在滚动时更改 ScrollView 内部视图的属性并通过滚动触发?
How to change properties of Views inside ScrollView while scrolling and triggered by scrolling?
我在我的应用程序中使用自定义贴靠 ScrollView。它扩展了 HorizontalScrollView 并覆盖了 onTouchEvent()。这用于在 ScrollView 中间捕捉子视图。还有一个前导和尾随 space,它们足够宽,因此第一个和最后一个元素可以放在中间。
我使用两个 class 变量:
int mActiveFeature //Position of the element nearest to the middle, initially set to 1
List<Integer> mAvailableItems //IDs of contained menu elements in order without the spaces
这是我的 onTouchEvent:
@Override
public boolean onTouchEvent(MotionEvent event) {
final int menuItemCount = this.mAvailableItems.size();
switch(event.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
float scrollX = getScrollX();
float layoutWidth =
SnappingScroll.this.getWidth();
float featureWidth =
(int) getResources().getDimension(R.dimen.item_width);
float spaceWidth =
(int) getResources().getDimension(R.dimen.space_width);
View oldItem =
findViewById(
SnappingScroll.this.mAvailableItems.get(
SnappingScroll.this.mActiveFeature - 1
)
);
oldItem.setAlpha(0.5f);
oldItem.setClickable(false);
SnappingScroll.this.mActiveFeature = (int) Math.min(
Math.round((scrollX - spaceWidth + (layoutWidth / 2) + (featureWidth / 2)) / featureWidth),
(float) menuItemCount
);
View newItem =
findViewById(
SnappingScroll.this.mAvailableItems.get(
SnappingScroll.this.mActiveFeature - 1
)
);
newItem.setAlpha(1.0f);
newItem.setClickable(true);
int scrollTo = (int) (
SnappingScroll.this.mActiveFeature * featureWidth
+ spaceWidth
- layoutWidth / 2
- featureWidth / 2
);
smoothScrollTo(scrollTo, 0);
return true;
default:
return super.onTouchEvent(event);
}
}
这是我正在使用的 XML 文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:orientation="vertical">
<com.example.SnappingScroll
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical">
<Space
android:id="@+id/leading_space"
android:layout_width="@dimen/space_width"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/item_1"
android:layout_width="@dimen/item_width"
android:layout_height="match_parent"
android:alpha="0.5"
android:clickable="false"
android:onClick="clickFunction"
android:src="@drawable/m_1"/>
<ImageView
android:id="@+id/item_2"
android:layout_width="@dimen/item_width"
android:layout_height="match_parent"
android:alpha="0.5"
android:clickable="false"
android:onClick="clickFunction"
android:src="@drawable/m_2"/>
<ImageView
android:id="@+id/item_3"
android:layout_width="@dimen/item_width"
android:layout_height="match_parent"
android:alpha="0.5"
android:clickable="false"
android:onClick="clickFunction"
android:src="@drawable/m_3"/>
<Space
android:id="@+id/trailing_space"
android:layout_width="@dimen/space_width"
android:layout_height="match_parent" />
</LinearLayout>
</com.example.SnappingScroll>
</LinearLayout>
我希望所有元素都半透明且不可点击,活动功能除外。目前,您可以滚动,当您松开时,视图会捕捉到最近的元素。那么最后吸附到的元素是deactivated,新吸附到的元素是activated。这工作正常,但我希望用户在抬起手指时看到 ScrollView 将吸附到哪个元素。因此,新活动特征的计算和透明度的改变应该在滚动时完成。这可能吗?我已经尝试将代码放在 MotionEvent.ACTION_MOVE 的案例中,但是视图根本没有滚动。当我将代码放入 MotionEvent.ACTION_SCROLL 的案例中时,没有任何反应,也没有触发捕捉。
编辑:
起初,我把代码放在一个额外的OnTouchListener中,这是多余的,所以现在我覆盖了onTouchEvent函数并相应地编辑了问题。
经过进一步阅读和一些尝试与错误后,我想出了解决方案。
所以首先,需要的MotionEvent是ACTION_MOVE。我以前试过这个,但没用,因为我自己 returned true 和 false,从不调用超类的实现。所以我总是 return super.onTouchEvent(event),因为它处理滚动本身。
只有我的 ACTION_UP 和 ACTION_CANCEL 处理没有调用超类,因为这会进行正常滚动并且不会啪啪啪。
@Override
public boolean onTouchEvent(MotionEvent event) {
final int menuItemCount = this.mAvailableItems.size();
int oldFeature = this.mActiveFeature;
float scrollX = getScrollX();
float layoutWidth =
SnappingScroll.this.getWidth();
float featureWidth =
(int) getResources().getDimension(R.dimen.item_width);
float spaceWidth =
(int) getResources().getDimension(R.dimen.space_width);
View newItem, oldItem;
switch(event.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
newItem =
findViewById(
SnappingScroll.this.mAvailableItems.get(
SnappingScroll.this.mActiveFeature - 1
)
);
newItem.setClickable(true);
int scrollTo = (int) (
SnappingScroll.this.mActiveFeature * featureWidth
+ spaceWidth
- layoutWidth / 2
- featureWidth / 2
);
smoothScrollTo(scrollTo, 0);
return true;
case MotionEvent.ACTION_MOVE:
SnappingScroll.this.mActiveFeature = (int) Math.min(
Math.round((scrollX - spaceWidth + (layoutWidth / 2) + (featureWidth / 2)) / featureWidth),
(float) menuItemCount
);
if(oldFeature == this.mActiveFeature) {
return super.onTouchEvent(event);
} else {
oldItem =
findViewById(
SnappingScroll.this.mAvailableItems.get(
oldFeature - 1
)
);
oldItem.setAlpha(0.5f);
oldItem.setClickable(false);
newItem =
findViewById(
SnappingScroll.this.mAvailableItems.get(
SnappingScroll.this.mActiveFeature - 1
)
);
newItem.setAlpha(1.0f);
return super.onTouchEvent(event);
}
default:
return super.onTouchEvent(event);
}
}
然后还有最后一个问题要解决。 ScrollView 可以翻转,因此当您抬起手指时,滚动不会停止。这干扰了捕捉,所以我覆盖了 fling() 方法:
@Override
public void fling (int velocityY)
{
super.fling(0);
}
现在,没有闪动,ScrollView 吸附到最中间的元素。透明度值是在滚动时设置的,因此用户始终知道 ScrollView 将吸附到哪个元素。
我在我的应用程序中使用自定义贴靠 ScrollView。它扩展了 HorizontalScrollView 并覆盖了 onTouchEvent()。这用于在 ScrollView 中间捕捉子视图。还有一个前导和尾随 space,它们足够宽,因此第一个和最后一个元素可以放在中间。
我使用两个 class 变量:
int mActiveFeature //Position of the element nearest to the middle, initially set to 1
List<Integer> mAvailableItems //IDs of contained menu elements in order without the spaces
这是我的 onTouchEvent:
@Override
public boolean onTouchEvent(MotionEvent event) {
final int menuItemCount = this.mAvailableItems.size();
switch(event.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
float scrollX = getScrollX();
float layoutWidth =
SnappingScroll.this.getWidth();
float featureWidth =
(int) getResources().getDimension(R.dimen.item_width);
float spaceWidth =
(int) getResources().getDimension(R.dimen.space_width);
View oldItem =
findViewById(
SnappingScroll.this.mAvailableItems.get(
SnappingScroll.this.mActiveFeature - 1
)
);
oldItem.setAlpha(0.5f);
oldItem.setClickable(false);
SnappingScroll.this.mActiveFeature = (int) Math.min(
Math.round((scrollX - spaceWidth + (layoutWidth / 2) + (featureWidth / 2)) / featureWidth),
(float) menuItemCount
);
View newItem =
findViewById(
SnappingScroll.this.mAvailableItems.get(
SnappingScroll.this.mActiveFeature - 1
)
);
newItem.setAlpha(1.0f);
newItem.setClickable(true);
int scrollTo = (int) (
SnappingScroll.this.mActiveFeature * featureWidth
+ spaceWidth
- layoutWidth / 2
- featureWidth / 2
);
smoothScrollTo(scrollTo, 0);
return true;
default:
return super.onTouchEvent(event);
}
}
这是我正在使用的 XML 文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/black"
android:orientation="vertical">
<com.example.SnappingScroll
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical">
<Space
android:id="@+id/leading_space"
android:layout_width="@dimen/space_width"
android:layout_height="match_parent" />
<ImageView
android:id="@+id/item_1"
android:layout_width="@dimen/item_width"
android:layout_height="match_parent"
android:alpha="0.5"
android:clickable="false"
android:onClick="clickFunction"
android:src="@drawable/m_1"/>
<ImageView
android:id="@+id/item_2"
android:layout_width="@dimen/item_width"
android:layout_height="match_parent"
android:alpha="0.5"
android:clickable="false"
android:onClick="clickFunction"
android:src="@drawable/m_2"/>
<ImageView
android:id="@+id/item_3"
android:layout_width="@dimen/item_width"
android:layout_height="match_parent"
android:alpha="0.5"
android:clickable="false"
android:onClick="clickFunction"
android:src="@drawable/m_3"/>
<Space
android:id="@+id/trailing_space"
android:layout_width="@dimen/space_width"
android:layout_height="match_parent" />
</LinearLayout>
</com.example.SnappingScroll>
</LinearLayout>
我希望所有元素都半透明且不可点击,活动功能除外。目前,您可以滚动,当您松开时,视图会捕捉到最近的元素。那么最后吸附到的元素是deactivated,新吸附到的元素是activated。这工作正常,但我希望用户在抬起手指时看到 ScrollView 将吸附到哪个元素。因此,新活动特征的计算和透明度的改变应该在滚动时完成。这可能吗?我已经尝试将代码放在 MotionEvent.ACTION_MOVE 的案例中,但是视图根本没有滚动。当我将代码放入 MotionEvent.ACTION_SCROLL 的案例中时,没有任何反应,也没有触发捕捉。
编辑:
起初,我把代码放在一个额外的OnTouchListener中,这是多余的,所以现在我覆盖了onTouchEvent函数并相应地编辑了问题。
经过进一步阅读和一些尝试与错误后,我想出了解决方案。
所以首先,需要的MotionEvent是ACTION_MOVE。我以前试过这个,但没用,因为我自己 returned true 和 false,从不调用超类的实现。所以我总是 return super.onTouchEvent(event),因为它处理滚动本身。
只有我的 ACTION_UP 和 ACTION_CANCEL 处理没有调用超类,因为这会进行正常滚动并且不会啪啪啪。
@Override
public boolean onTouchEvent(MotionEvent event) {
final int menuItemCount = this.mAvailableItems.size();
int oldFeature = this.mActiveFeature;
float scrollX = getScrollX();
float layoutWidth =
SnappingScroll.this.getWidth();
float featureWidth =
(int) getResources().getDimension(R.dimen.item_width);
float spaceWidth =
(int) getResources().getDimension(R.dimen.space_width);
View newItem, oldItem;
switch(event.getAction()) {
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
newItem =
findViewById(
SnappingScroll.this.mAvailableItems.get(
SnappingScroll.this.mActiveFeature - 1
)
);
newItem.setClickable(true);
int scrollTo = (int) (
SnappingScroll.this.mActiveFeature * featureWidth
+ spaceWidth
- layoutWidth / 2
- featureWidth / 2
);
smoothScrollTo(scrollTo, 0);
return true;
case MotionEvent.ACTION_MOVE:
SnappingScroll.this.mActiveFeature = (int) Math.min(
Math.round((scrollX - spaceWidth + (layoutWidth / 2) + (featureWidth / 2)) / featureWidth),
(float) menuItemCount
);
if(oldFeature == this.mActiveFeature) {
return super.onTouchEvent(event);
} else {
oldItem =
findViewById(
SnappingScroll.this.mAvailableItems.get(
oldFeature - 1
)
);
oldItem.setAlpha(0.5f);
oldItem.setClickable(false);
newItem =
findViewById(
SnappingScroll.this.mAvailableItems.get(
SnappingScroll.this.mActiveFeature - 1
)
);
newItem.setAlpha(1.0f);
return super.onTouchEvent(event);
}
default:
return super.onTouchEvent(event);
}
}
然后还有最后一个问题要解决。 ScrollView 可以翻转,因此当您抬起手指时,滚动不会停止。这干扰了捕捉,所以我覆盖了 fling() 方法:
@Override
public void fling (int velocityY)
{
super.fling(0);
}
现在,没有闪动,ScrollView 吸附到最中间的元素。透明度值是在滚动时设置的,因此用户始终知道 ScrollView 将吸附到哪个元素。