设置view只有半边圆角

实现指定的边切圆角,使用UIBezierPath

/*设置顶部圆角*/
- (void)setCornerOnTop:(CGFloat )cornerRadius {
    UIBezierPath *maskPath;
    maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                     byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerTopRight)
                                           cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = self.bounds;
    maskLayer.path = maskPath.CGPath;
    self.layer.mask = maskLayer;
}
/*设置底部圆角*/
- (void)setCornerOnBottom:(CGFloat )cornerRadius {
    UIBezierPath *maskPath;
    maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                     byRoundingCorners:(UIRectCornerBottomLeft | UIRectCornerBottomRight)
                                           cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = self.bounds;
    maskLayer.path = maskPath.CGPath;
    self.layer.mask = maskLayer;
}
/*设置左边圆角*/
- (void)setCornerOnLeft:(CGFloat )cornerRadius {
    UIBezierPath *maskPath;
    maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                     byRoundingCorners:(UIRectCornerTopLeft | UIRectCornerBottomLeft)
                                           cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = self.bounds;
    maskLayer.path = maskPath.CGPath;
    self.layer.mask = maskLayer;
}
/*设置右边圆角*/
- (void)setCornerOnRight:(CGFloat )cornerRadius {
    UIBezierPath *maskPath;
    maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                     byRoundingCorners:(UIRectCornerTopRight | UIRectCornerBottomRight)
                                           cornerRadii:CGSizeMake(cornerRadius, cornerRadius)];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = self.bounds;
    maskLayer.path = maskPath.CGPath;
    self.layer.mask = maskLayer;
}
/*设置四边圆角*/
- (void)setAllCorner {
    UIBezierPath *maskPath;
    maskPath = [UIBezierPath bezierPathWithRoundedRect:self.bounds
                                          cornerRadius:10.0];
    CAShapeLayer *maskLayer = [[CAShapeLayer alloc] init];
    maskLayer.frame = self.bounds;
    maskLayer.path = maskPath.CGPath;
    self.layer.mask = maskLayer;
}
public class HomeFragment extends BaseFragment { RecyclerView rv,sr_rv, qy_rv, xz_rv; ImageView iv_ss, iv_gd, sr_start, qy_start, xz_start,iv_bf; TextView tv_time,tv_song_name; private MediaPlayer mediaPlayer; private int currentPlayingPosition = -1; // 初始值设为 -1,表示没有播放 private boolean isScrollInProgress = false; // 防止重复滚动 private ObjectAnimator rotationAnimator; private SongTopAdapter songTopAdapter; private ImageView currentRotationView; private boolean isPlayingState = false; // 记录当前是否处于“正在播放”的逻辑状态 private boolean wasPlayingBeforeScroll = false; // 是否在滑动前是播放状态 @Override protected int setLayout() { return R.layout.fragment_home; } @Override protected void initView() { iv_ss = fvbi(R.id.iv_ss); iv_gd = fvbi(R.id.iv_gd); iv_bf = fvbi(R.id.iv_bf); rv = fvbi(R.id.rv); sr_rv = fvbi(R.id.sr_rv); qy_rv = fvbi(R.id.qy_rv); xz_rv = fvbi(R.id.xz_rv); sr_start = fvbi(R.id.sr_start); qy_start = fvbi(R.id.qy_start); xz_start = fvbi(R.id.xz_start); tv_time = fvbi(R.id.tv_time); tv_song_name = fvbi(R.id.tv_song_name); tv_song_name.setEllipsize(TextUtils.TruncateAt.MARQUEE); tv_song_name.setSingleLine(true); tv_song_name.setSelected(true); tv_song_name.setFocusable(true); tv_song_name.setFocusableInTouchMode(true); } @Override protected void initClick() { //跳转搜索页面 iv_ss.setOnClickListener(new OnMultiClickListener() { @Override public void onMultiClick(View v) { toClass(SearchActivity.class); } }); // 设置点击事件:切换播放/暂停 iv_bf.setOnClickListener(v -> { if (mediaPlayer == null) return; if (mediaPlayer.isPlaying()) { // 当前正在播放 → 暂停 mediaPlayer.pause(); isPlayingState = false; iv_bf.setImageResource(R.mipmap.img_lan_bf); // 显示“播放”图标 stopRotationAnimation(); // 停止旋转动画 ToastUtil.showShortToast("已暂停"); } else { // 当前暂停 → 继续播放 mediaPlayer.start(); isPlayingState = true; iv_bf.setImageResource(R.mipmap.img_lan_zt); // 显示“暂停”图标 // 启动旋转:需要获取当前播放项的封面 // startRotationForCurrentItem(currentPlayingPosition); ToastUtil.showShortToast("继续播放"); } }); } @Override protected void initData() { Calendar calendar = Calendar.getInstance(); int hour = calendar.get(Calendar.HOUR_OF_DAY); // 获取 24 小时制的小时 String greeting; if (hour >= 5 && hour < 11) { greeting = "HI,早上好"; } else if (hour == 11) { greeting = "HI,中午好"; } else if (hour == 12) { greeting = "HI,下午好"; } else if (hour >= 13 && hour < 18) { greeting = "HI,下午好"; } else { // hour >= 18 或 hour < 5(凌晨) greeting = "HI,晚上好"; } tv_time.setText(greeting); musicAppInfo(); } private void musicAppInfo() { HashMap<String, String> map = new HashMap<>(); showDialog(); HttpBuiler.getInfo(Url.music, map).build().execute( new GenericsCallback<MusicBean>(new JsonGenericsSerializator()) { @Override public void onError(Call call, Exception e, int id) { dissmiss(); ToastUtil.showShortToast(e.getMessage()); } @Override public void onResponse(MusicBean response, int id) { dissmiss(); if (response.getCode().equals("0")) { if (null != response.getData() && response.getData().size() > 0) { // 1. 创建 Adapter songTopAdapter = new SongTopAdapter(); // 2. 设置布局管理器和适配器 rv.setLayoutManager(new LinearLayoutManager(getActivity(), LinearLayoutManager.HORIZONTAL, false)); rv.setAdapter(songTopAdapter); // 3. 【关键】先设置播放状态监听器(必须在 setup 前!) songTopAdapter.setOnPlayStateChangedListener((position, coverView) -> { Log.d("Rotation", "收到播放通知: position=" + position); stopRotationAnimation(); // 停止旧动画 startRotationAnimation(coverView); // 启动新动画 }); // 4. 【重要】现在才能调用 setupSnappingAndPlayback // 因为这里面会触发滚动检测 → 可能立即调用 playMusicAtPosition → 需要监听器已准备好! setupSnappingAndPlayback(rv, songTopAdapter); // 5. 最后设置数据(避免提前触发 convert 导致空指针) songTopAdapter.setList(response.getData()); } else { ToastUtil.showShortToast("暂无歌曲数据"); } } else { ToastUtil.showShortToast(response.getMessage()); } } }); } // ================== 滚动监听 + 自动播放 ================== private void setupSnappingAndPlayback(RecyclerView rv, SongTopAdapter adapter) { LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); if (layoutManager == null) return; int fixedTargetX = dp2px(50f); // 模糊图右边界 // 使用 SnapHelper(保留吸附动画) FixedSideSnapHelper snapHelper = new FixedSideSnapHelper(fixedTargetX); snapHelper.attachToRecyclerView(rv); // 新增:记录已触发播放的位置,防止重复 final AtomicInteger lastPlayedPosition = new AtomicInteger(RecyclerView.NO_POSITION); rv.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); int pos = findBestObscuredItem(recyclerView, fixedTargetX); if (pos != RecyclerView.NO_POSITION && pos != lastPlayedPosition.get()) { playMusicAtPosition(pos, adapter.getData()); lastPlayedPosition.set(pos); } dispatchVisualFeedback(recyclerView, adapter); } @Override public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) { super.onScrollStateChanged(recyclerView, newState); if (newState == RecyclerView.SCROLL_STATE_DRAGGING || newState == RecyclerView.SCROLL_STATE_SETTLING) { // 用户开始滑动或惯性滑动 → 暂停音乐和动画 if (mediaPlayer != null && mediaPlayer.isPlaying()) { mediaPlayer.pause(); wasPlayingBeforeScroll = true; // 记录原来是播放状态 stopRotationAnimation(); iv_bf.setImageResource(R.mipmap.img_lan_bf); // 更新按钮图标 Log.d("ScrollPause", "滑动中暂停音乐"); } else { wasPlayingBeforeScroll = false; } isScrollInProgress = true; } if (newState == RecyclerView.SCROLL_STATE_IDLE) { // 滑动结束 → 如果原来是播放状态,则恢复 isScrollInProgress = false; if (wasPlayingBeforeScroll && mediaPlayer != null) { mediaPlayer.start(); iv_bf.setImageResource(R.mipmap.img_lan_zt); startRotationForCurrentItem(currentPlayingPosition); Log.d("ScrollResume", "滑动结束,恢复播放"); } } } }); setupOneWayScroll(rv, rv.getContext()); } private void dispatchVisualFeedback(RecyclerView rv, SongTopAdapter adapter) { LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); if (layoutManager == null) return; int targetX = dp2px(100f); // 模糊图右侧位置 for (int i = 0; i < rv.getChildCount(); i++) { View child = rv.getChildAt(i); if (child == null) continue; int left = layoutManager.getDecoratedLeft(child); int right = layoutManager.getDecoratedRight(child); int centerX = (left + right) / 2; float distance = Math.abs(centerX - targetX); // 只处理附近的 item if (distance > 600) continue; float alpha = 1.0f - Math.min(1.0f, distance / 300f); float scale = 0.8f + 0.2f * (1.0f - Math.min(1.0f, distance / 200f)); // child.setAlpha(alpha); // child.setScaleX(scale); // child.setScaleY(scale); } } /** * 查找当前是否有 item 被模糊图遮挡其左半部分约 50% * 假设模糊图固定显示在 [0, fuzzyRightEdge] 区域内(例如宽度 100dp) */ private int findBestObscuredItem(RecyclerView rv, int fuzzyRightEdge) { LinearLayoutManager layoutManager = (LinearLayoutManager) rv.getLayoutManager(); if (layoutManager == null) return RecyclerView.NO_POSITION; int childCount = layoutManager.getChildCount(); int bestPos = RecyclerView.NO_POSITION; float minDiffToHalf = Float.MAX_VALUE; for (int i = 0; i < childCount; i++) { View child = layoutManager.getChildAt(i); if (child == null) continue; int left = layoutManager.getDecoratedLeft(child); int right = layoutManager.getDecoratedRight(child); int width = right - left; // 必须有交集:item 必须与模糊图区域 [0, fuzzyRightEdge] 相交 if (right <= 0 || left >= fuzzyRightEdge) continue; // 无交集 // 计算重叠区域:模糊图 [0, fuzzyRightEdge] 与 item [left, right] int overlapStart = Math.max(left, 0); int overlapEnd = Math.min(right, fuzzyRightEdge); int overlapWidth = Math.max(0, overlapEnd - overlapStart); // 计算被遮挡的比例(仅左半部分被遮才算) float coveredRatio = (float) overlapWidth / width; // 我们希望刚好遮住一半(左半边) float diff = Math.abs(coveredRatio - 0.5f); // 筛选合理范围内的遮挡(避免噪声) if (coveredRatio >= 0.3f && coveredRatio <= 0.7f && diff < minDiffToHalf) { minDiffToHalf = diff; bestPos = layoutManager.getPosition(child); } } return bestPos; } private void setupOneWayScroll(RecyclerView rv, Context context) { final boolean[] hasScrolledLeft = {false}; // 标记是否已左滑 final float[] startX = {0f}; rv.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() { @Override public boolean onInterceptTouchEvent(@NonNull RecyclerView recyclerView, @NonNull MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: startX[0] = e.getX(); break; case MotionEvent.ACTION_MOVE: float currentX = e.getX(); float dx = currentX - startX[0]; // 手指移动距离 if (dx < -20 && !hasScrolledLeft[0]) { // 用户向左滑动(内容向右移出视野),视为“已前进” hasScrolledLeft[0] = true; } // 如果已经左滑过,则禁止任何右滑(手指向右拖) if (hasScrolledLeft[0] && dx > 15) { // 检测到右滑意图 → 拦截事件,不交给 RecyclerView Toast.makeText(context, "暂只支持左滑操作", Toast.LENGTH_SHORT).show(); return true; // 拦截触摸事件,防止 RecyclerView 滚动 } break; } return false; // 正常处理其他情况 } }); } private void playMusicAtPosition(int position, List<MusicBean.DataBean> dataList) { if (position == RecyclerView.NO_POSITION || position >= dataList.size()) return; MusicBean.DataBean bean = dataList.get(position); currentPlayingPosition = position; try { if (mediaPlayer == null) { mediaPlayer = new MediaPlayer(); } else { if (mediaPlayer.isPlaying()) mediaPlayer.stop(); mediaPlayer.reset(); mediaPlayer.setOnPreparedListener(null); mediaPlayer.setOnCompletionListener(null); } mediaPlayer.setDataSource(bean.getMusic()); mediaPlayer.setAudioAttributes(new AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) .setUsage(AudioAttributes.USAGE_MEDIA) .build()); mediaPlayer.prepareAsync(); mediaPlayer.setOnPreparedListener(mp -> { mp.start(); isPlayingState = true; iv_bf.setImageResource(R.mipmap.img_lan_zt); // 播放时显示暂停图标 }); mediaPlayer.setOnCompletionListener(mp -> { Log.d("AutoPlay", "歌曲播放完成: " + bean.getTitle()); playNextSong(dataList); }); // 通知 Adapter 刷新界面(触发 convert) songTopAdapter.updatePlayingPosition(position); // 尝试获取当前可见的 itemView 上的 ImageView 并启动旋转 // startRotationForCurrentItem(position); Log.d("AutoPlay", "正在播放: " + bean.getTitle()); ToastUtil.showShortToast("播放: " + bean.getTitle()); tv_song_name.setText(bean.getTitle()+"-"+bean.getSinger()); // 在 playMusicAtPosition 方法末尾 iv_bf.setImageResource(R.mipmap.img_lan_zt); // 新歌开始播,设为暂停图标 } catch (Exception e) { e.printStackTrace(); ToastUtil.showShortToast("无法播放"); playNextSong(dataList); } } private void playNextSong(List<MusicBean.DataBean> dataList) { int nextPos = currentPlayingPosition + 1; if (nextPos < dataList.size()) { playMusicAtPosition(nextPos, dataList); scrollToTriggerPosition(nextPos, rv); // 自动滚动到触发位置 } else { ToastUtil.showShortToast("已播放完全部歌曲"); } } private void scrollToTriggerPosition(int targetPos, RecyclerView recyclerView) { LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager(); if (layoutManager == null) return; LinearSmoothScroller scroller = new LinearSmoothScroller(recyclerView.getContext()) { private static final float TARGET_OFFSET = 80f; // 调整此值 @Override public PointF computeScrollVectorForPosition(int targetPosition) { return layoutManager.computeScrollVectorForPosition(targetPosition); } @Override public int calculateDxToMakeVisible(View view, int snapPreference) { // 使用 layoutManager 实例的方法 int left = layoutManager.getDecoratedLeft(view); int right = layoutManager.getDecoratedRight(view); int center = (left + right) / 2; int targetX = dp2px(50f); // 模糊图右边界的 X 坐标 return targetX - center; // 偏移量 } @Override protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { return 150f / displayMetrics.densityDpi; } }; scroller.setTargetPosition(targetPos); layoutManager.startSmoothScroll(scroller); } private int dp2px(float dp) { return (int) TypedValue.applyDimension( TypedValue.COMPLEX_UNIT_DIP, dp, rv.getResources().getDisplayMetrics() ); } /** * 启动封面旋转动画 */ private void startRotationAnimation(ImageView imageView) { if (imageView == null || imageView.getDrawable() == null) return; // 先清除旧动画 if (rotationAnimator != null && rotationAnimator.isRunning()) { rotationAnimator.cancel(); } // 方法一:使用 ObjectAnimator(保留,但需设置 LayerType) rotationAnimator = ObjectAnimator.ofFloat(imageView, "rotation", 0f, 360f); rotationAnimator.setDuration(10000); // 10秒一圈 rotationAnimator.setRepeatCount(ObjectAnimator.INFINITE); rotationAnimator.setInterpolator(new LinearInterpolator()); // 关键一步:启用硬件层加速(否则某些设备上动画卡顿或不显示) imageView.setLayerType(View.LAYER_TYPE_HARDWARE, null); rotationAnimator.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { imageView.setLayerType(View.LAYER_TYPE_NONE, null); } }); rotationAnimator.start(); } /** * 停止旋转动画 */ private void stopRotationAnimation() { if (rotationAnimator != null) { rotationAnimator.cancel(); rotationAnimator = null; } if (currentRotationView != null) { currentRotationView.setLayerType(View.LAYER_TYPE_NONE, null); currentRotationView = null; } } /** * 根据 position 手动启动该 itemView 的旋转动画(用于恢复播放) */ private void startRotationForCurrentItem(int position) { if (position == RecyclerView.NO_POSITION || songTopAdapter == null) return; RecyclerView.ViewHolder viewHolder = rv.findViewHolderForAdapterPosition(position); if (viewHolder != null) { ImageView coverView = viewHolder.itemView.findViewById(R.id.iv_song_logo); // 替换为你的封面ID if (coverView != null) { stopRotationAnimation(); // 安全起见先停 startRotationAnimation(coverView); } } else { // 如果 item 不可见,通过 adapter 回调间接启动(convert 会触发) Log.d("Rotation", "目标 itemView 不可见,等待 convert 触发"); } } @Override public void onDestroy() { if (mediaPlayer != null) { if (mediaPlayer.isPlaying()) mediaPlayer.stop(); mediaPlayer.release(); mediaPlayer = null; } super.onDestroy(); } } public class SongTopAdapter extends RecyclerView.Adapter<SongTopAdapter.DeliveryViewHolder> { private List<MusicBean.DataBean> dataList = new ArrayList<>(); private int currentPlayingPosition = -1; // 播放状态监听器 public interface OnPlayStateChangedListener { void onPlayingAtPosition(int position, ImageView coverView); } private OnPlayStateChangedListener playStateListener; public void setOnPlayStateChangedListener(OnPlayStateChangedListener listener) { this.playStateListener = listener; } // 设置数据(替代 setList) public void setList(List<MusicBean.DataBean> list) { this.dataList.clear(); if (list != null) { this.dataList.addAll(list); } notifyDataSetChanged(); } public List<MusicBean.DataBean> getData() { return dataList; } public int getCurrentPlayingPosition() { return currentPlayingPosition; } public void updatePlayingPosition(int pos) { int oldPos = currentPlayingPosition; currentPlayingPosition = pos; // 通知旧位置和新位置刷新 if (oldPos >= 0 && oldPos < dataList.size()) { notifyItemChanged(oldPos); } if (pos >= 0 && pos < dataList.size()) { notifyItemChanged(pos); } } // ViewHolder 类 public static class DeliveryViewHolder extends RecyclerView.ViewHolder { ImageView ivCover; public DeliveryViewHolder(@NonNull View itemView) { super(itemView); ivCover = itemView.findViewById(R.id.iv_song_logo); } } @NonNull @Override public DeliveryViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.song_top_adapter, parent, false); return new DeliveryViewHolder(view); } @Override public void onBindViewHolder(@NonNull DeliveryViewHolder holder, int position) { MusicBean.DataBean dataBean = dataList.get(position); // 使用 Glide 加载封面 Glide.with(holder.itemView.getContext()) .load(dataBean.getCover()) .transform(new CircleCrop()) .error(getErrorDrawable(holder.itemView.getContext())) .into(holder.ivCover); boolean isPlaying = (position == currentPlayingPosition); if (isPlaying && playStateListener != null) { holder.ivCover.post(() -> playStateListener.onPlayingAtPosition(position, holder.ivCover)); } } @Override public int getItemCount() { return dataList.size(); } // 静态错误图缓存(防止重复创建) private static BitmapDrawable sErrorDrawable; private BitmapDrawable getErrorDrawable(Context context) { if (sErrorDrawable == null) { Bitmap bitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.img_home_sing); Bitmap circularBitmap = ImageUtils.getCircularBitmap(bitmap); sErrorDrawable = new BitmapDrawable(context.getResources(), circularBitmap); } return sErrorDrawable; } } 保持现有逻辑,其中有两张歌曲图片是因为添加的错误图片没有旋转动画,怎么修改让他也有旋转动画 .error(getErrorDrawable(holder.itemView.getContext()))
最新发布
12-03
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值