如何在屏幕外剪辑 ImageView 角(圆角)
How to clip ImageView's corner's (rounded corners) off-screen
我已将四角圆角的图像加载到视图寻呼机中。但是,出于设计原因,我只希望在 viewpager 稳定时使顶角变圆,但在更改页面时让所有角都变圆。我通过四舍五入并将图像向下推约 8dp 来实现这一点。
我遇到的问题是,即使我使用 glide 的 .transform(new RoundedCorners(8))
将所有角都圆化了,当滑动 viewpager 时,它只显示顶角为圆角。我已经发布了一些屏幕截图:
ViewPager入驻:
ViewPager 移动:
我应该提一下,我正在使用 PageTransformer 为页面之间的过渡设置动画,这就是第二个屏幕截图中页面缩小的原因。
这是完整的 Glide 代码:
Glide.with(albumArt)
.load(trackModel.getAlbumCoverArtUrl())
.apply(new RequestOptions()
.fitCenter()
.transform(new RoundedCorners(8)))
.into(albumArt);
我也试过为 ImageView 定义轮廓并设置 setClipToOutline(true)
,如下所示:
albumArt.setClipToOutline(true);
albumArt.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), 16);
}
});
但是这导致以下结果不是我想要的:当 ViewPager 稳定时所有的角都变圆了:
我尝试更改 XML 中 ImageView 上的 layout_margins 以及 ImageView 的容器片段,但是当 ViewPager 稳定时,这总是使所有角都变圆并且可见。
我希望所有的角都变成圆角,但是当 ViewPager 设置为底角 "pushed" 离开屏幕时,您只能看到顶角圆形。当 ViewPager 被滑动时,我希望所有的角都可见且圆润。
这是完整的 class 和 XML:
Class:
public class NowPlayingPageFragment extends Fragment {
private static final String TAG = "NowPlayingPageFragment";
public static NowPlayingPageFragment newInstance(TrackModel trackModel) {
NowPlayingPageFragment fragment = new NowPlayingPageFragment();
Bundle argument = new Bundle();
argument.putSerializable(TrackModel.class.getSimpleName(), trackModel);
fragment.setArguments(argument);
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.now_playing_page, container, false);
Bundle arguments = getArguments();
TrackModel trackModel = (TrackModel) arguments.getSerializable(TrackModel.class.getSimpleName());
Log.d(TAG, "NowPlayingPage: " + trackModel.getAlbumCoverArtUrl());
ImageView albumArt = rootView.findViewById(R.id.nowPlayingAlbumArtPage);
// Alternate method to round corners
// albumArt.setClipToOutline(true);
// albumArt.setOutlineProvider(new ViewOutlineProvider() {
// @Override
// public void getOutline(View view, Outline outline) {
// outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), 16);
// }
// });
Log.d(TAG, "NowPlayingPage: setClipToOutline" + albumArt.getClipToOutline());
Glide.with(albumArt)
.load(trackModel.getAlbumCoverArtUrl())
.apply(new RequestOptions()
.fitCenter()
.transform(new RoundedCorners(8))
)
.into(albumArt);
return rootView;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
应该提到布局嵌套在嵌套在 bottomsheet 布局中的 viewpager 片段布局中。
XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="0dp"
android:background="@drawable/round_corner_dialog"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="horizontal">
<ImageView
android:id="@+id/nowPlayingAlbumArtPage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:contentDescription="@string/album_art_large"
android:cropToPadding="false"
android:scaleType="centerCrop"
android:visibility="visible" />
</LinearLayout>
我可以通过调整布局的平移和缩放属性来解决这个问题!
任何想知道如何做的人,请看看这个项目。 ViewPager2Demo
诀窍是在 transformPage
之间交替的 2 个视图始终具有相同的值
cap
保证在提供一致性的任一视图中具有介于 0 和 0.5 之间的值
val cap = min(abs(position), abs(1 - position)).toDouble()
private fun expPlateau(ym: Double, y0: Double, k: Double, x: Double): Double {
return ym - (ym - y0) * exp(-k * x)
}
对于已经熟悉 ViewPager2 的人来说 class。
class ZoomOutPageTransformer : ViewPager2.PageTransformer {
/**
* The position is fed to this function with an alternating order between the 2 views showing on the screen
*/
override fun transformPage(view: View, position: Float) {
view.apply {
when {
position < -1 -> { // [-Infinity,-1)
// This page is way off-screen to the left.
alpha = 0f
}
position <= 1 -> { // [-1,1]
val scaleFactor = max(MIN_SCALE, 1 - abs(position))
// to always get the same value - this will essentially cap it at 0.5
val cap = min(abs(position), abs(1 - position)).toDouble()
// Exponential plateau function
val radius = expPlateau(YM,Y0, K, cap).toFloat()
// Scale the page down (between MIN_SCALE and 1)
scaleX = scaleFactor
scaleY = scaleFactor
setCornerRadius(view, radius)
}
else -> { // (1,+Infinity]
// This page is way off-screen to the right.
alpha = 0f
}
}
}
}
private fun setCornerRadius(view: View, radius: Float) {
val drawable = (((view as FrameLayout).children.first() as ConstraintLayout).children.first().background as GradientDrawable)
drawable.cornerRadius = radius
(view.children.first() as ConstraintLayout).children.first().background = drawable
}
/**
* y0 is the starting population (same units as y)
* ym is the maximum population (same units as y)
* k determines is the rate constant (inverse of x units)
*/
private fun expPlateau(ym: Double, y0: Double, k: Double, x: Double): Double {
return ym - (ym - y0) * exp(-k * x)
}
}
我已将四角圆角的图像加载到视图寻呼机中。但是,出于设计原因,我只希望在 viewpager 稳定时使顶角变圆,但在更改页面时让所有角都变圆。我通过四舍五入并将图像向下推约 8dp 来实现这一点。
我遇到的问题是,即使我使用 glide 的 .transform(new RoundedCorners(8))
将所有角都圆化了,当滑动 viewpager 时,它只显示顶角为圆角。我已经发布了一些屏幕截图:
ViewPager入驻:
ViewPager 移动:
我应该提一下,我正在使用 PageTransformer 为页面之间的过渡设置动画,这就是第二个屏幕截图中页面缩小的原因。 这是完整的 Glide 代码:
Glide.with(albumArt)
.load(trackModel.getAlbumCoverArtUrl())
.apply(new RequestOptions()
.fitCenter()
.transform(new RoundedCorners(8)))
.into(albumArt);
我也试过为 ImageView 定义轮廓并设置 setClipToOutline(true)
,如下所示:
albumArt.setClipToOutline(true);
albumArt.setOutlineProvider(new ViewOutlineProvider() {
@Override
public void getOutline(View view, Outline outline) {
outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), 16);
}
});
但是这导致以下结果不是我想要的:当 ViewPager 稳定时所有的角都变圆了:
我尝试更改 XML 中 ImageView 上的 layout_margins 以及 ImageView 的容器片段,但是当 ViewPager 稳定时,这总是使所有角都变圆并且可见。
我希望所有的角都变成圆角,但是当 ViewPager 设置为底角 "pushed" 离开屏幕时,您只能看到顶角圆形。当 ViewPager 被滑动时,我希望所有的角都可见且圆润。
这是完整的 class 和 XML:
Class:
public class NowPlayingPageFragment extends Fragment {
private static final String TAG = "NowPlayingPageFragment";
public static NowPlayingPageFragment newInstance(TrackModel trackModel) {
NowPlayingPageFragment fragment = new NowPlayingPageFragment();
Bundle argument = new Bundle();
argument.putSerializable(TrackModel.class.getSimpleName(), trackModel);
fragment.setArguments(argument);
return fragment;
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
Bundle savedInstanceState) {
ViewGroup rootView = (ViewGroup) inflater.inflate(
R.layout.now_playing_page, container, false);
Bundle arguments = getArguments();
TrackModel trackModel = (TrackModel) arguments.getSerializable(TrackModel.class.getSimpleName());
Log.d(TAG, "NowPlayingPage: " + trackModel.getAlbumCoverArtUrl());
ImageView albumArt = rootView.findViewById(R.id.nowPlayingAlbumArtPage);
// Alternate method to round corners
// albumArt.setClipToOutline(true);
// albumArt.setOutlineProvider(new ViewOutlineProvider() {
// @Override
// public void getOutline(View view, Outline outline) {
// outline.setRoundRect(0, 0, view.getWidth(), view.getHeight(), 16);
// }
// });
Log.d(TAG, "NowPlayingPage: setClipToOutline" + albumArt.getClipToOutline());
Glide.with(albumArt)
.load(trackModel.getAlbumCoverArtUrl())
.apply(new RequestOptions()
.fitCenter()
.transform(new RoundedCorners(8))
)
.into(albumArt);
return rootView;
}
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
}
应该提到布局嵌套在嵌套在 bottomsheet 布局中的 viewpager 片段布局中。
XML:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="0dp"
android:background="@drawable/round_corner_dialog"
android:clipChildren="false"
android:clipToPadding="false"
android:orientation="horizontal">
<ImageView
android:id="@+id/nowPlayingAlbumArtPage"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
android:contentDescription="@string/album_art_large"
android:cropToPadding="false"
android:scaleType="centerCrop"
android:visibility="visible" />
</LinearLayout>
我可以通过调整布局的平移和缩放属性来解决这个问题!
任何想知道如何做的人,请看看这个项目。 ViewPager2Demo
诀窍是在 transformPage
cap
保证在提供一致性的任一视图中具有介于 0 和 0.5 之间的值
val cap = min(abs(position), abs(1 - position)).toDouble()
private fun expPlateau(ym: Double, y0: Double, k: Double, x: Double): Double {
return ym - (ym - y0) * exp(-k * x)
}
对于已经熟悉 ViewPager2 的人来说 class。
class ZoomOutPageTransformer : ViewPager2.PageTransformer {
/**
* The position is fed to this function with an alternating order between the 2 views showing on the screen
*/
override fun transformPage(view: View, position: Float) {
view.apply {
when {
position < -1 -> { // [-Infinity,-1)
// This page is way off-screen to the left.
alpha = 0f
}
position <= 1 -> { // [-1,1]
val scaleFactor = max(MIN_SCALE, 1 - abs(position))
// to always get the same value - this will essentially cap it at 0.5
val cap = min(abs(position), abs(1 - position)).toDouble()
// Exponential plateau function
val radius = expPlateau(YM,Y0, K, cap).toFloat()
// Scale the page down (between MIN_SCALE and 1)
scaleX = scaleFactor
scaleY = scaleFactor
setCornerRadius(view, radius)
}
else -> { // (1,+Infinity]
// This page is way off-screen to the right.
alpha = 0f
}
}
}
}
private fun setCornerRadius(view: View, radius: Float) {
val drawable = (((view as FrameLayout).children.first() as ConstraintLayout).children.first().background as GradientDrawable)
drawable.cornerRadius = radius
(view.children.first() as ConstraintLayout).children.first().background = drawable
}
/**
* y0 is the starting population (same units as y)
* ym is the maximum population (same units as y)
* k determines is the rate constant (inverse of x units)
*/
private fun expPlateau(ym: Double, y0: Double, k: Double, x: Double): Double {
return ym - (ym - y0) * exp(-k * x)
}
}