[Android 15] 해결방법 targetSdk 35 :: SnackbarLayout can only be accessed from within the same library group (referenced groupId=com.google.android.material from groupId=…)
이 오류는 Android의 Snackbar.SnackbarLayout
클래스를 직접 참조했기 때문에 발생한 것이다.
Android 13 (API 33) 이상, 특히 targetSdkVersion 34+ (예: 35에서는 더 엄격해짐)에서는 @RestrictTo(LIBRARY_GROUP)
로 지정된 내부 클래스에 접근하는 것이 금지된다.
해당 클래스는 com.google.android.material
라이브러리 내부에서만 사용되어야 한다.
현재 문제 코드
Snackbar.SnackbarLayout layout = (Snackbar.SnackbarLayout) globalSnackbar.getView();
SnackbarLayout
은 내부 전용 API로, 외부 앱에서 직접 사용할 수 없다.
해결 방법
Snackbar.getView()
로 반환되는 뷰에 대해 View 타입으로만 접근
Snackbar.getView()
는 View
타입을 반환하므로, 내부 클래스에 캐스팅하지 말고 그냥 View
로 쓰자.
View layout = globalSnackbar.getView();
layout.setPadding(0, 0, 0, 0); // 필요에 따라 setPadding 가능
추가 커스터마이징이 필요할 경우?
Snackbar
의 레이아웃을 더 복잡하게 커스터마이징하려면:
Snackbar
대신 Custom Toast, BottomSheetDialog, 또는 Custom View를 사용하는 것을 추천.- 또는
Snackbar
의 기본 메시지 뷰(TextView
)에 접근하려면 아래처럼 사용할 수 있다
TextView textView = layout.findViewById(com.google.android.material.R.id.snackbar_text);
textView.setTextSize(18); // 텍스트 크기 변경 예시
정리
항목 | 조치 |
---|---|
🚫내부 클래스 접근 | Snackbar.SnackbarLayout 직접 참조 금지 |
대체 방법 | View layout = snackbar.getView(); |
커스터마이즈 | layout.findViewById(...) 등으로 내부 요소만 접근 |
필요시 대안 | Custom Toast, BottomSheetDialog 등 고려 |
CustomSnackbarView 만들기
필요하다면 커스터마이즈 가능한 Snackbar
대체 뷰를 직접 만들어볼 수 있다.
기존 Snackbar
대신 사용할 수 있는 커스터마이즈 가능한 대체 뷰 (CustomSnackbarView) 를 만들어 보자. Toast
보다 자유도가 높고 Snackbar
처럼 하단에 나타나며, 버튼이나 애니메이션도 추가할 수 있도록 구성한다.
목표
- 하단에 슬라이드처럼 올라오는 알림
- 메시지 + 버튼 (옵션)
- 일정 시간 후 자동 사라짐
- 스타일, 색상, 애니메이션 자유롭게 커스터마이징 가능
CustomSnackbar.java
import android.app.Activity;
import android.os.Handler;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.TranslateAnimation;
import android.widget.Button;
import android.widget.TextView;
public class CustomSnackbar {
private final View snackbarView;
private final Handler handler = new Handler();
public CustomSnackbar(Activity activity, String message, String buttonText, View.OnClickListener action) {
LayoutInflater inflater = activity.getLayoutInflater();
snackbarView = inflater.inflate(R.layout.view_custom_snackbar, null);
TextView messageView = snackbarView.findViewById(R.id.snackbar_text);
messageView.setText(message);
Button actionButton = snackbarView.findViewById(R.id.snackbar_action);
if (buttonText != null && action != null) {
actionButton.setText(buttonText);
actionButton.setOnClickListener(v -> {
action.onClick(v);
dismiss();
});
} else {
actionButton.setVisibility(View.GONE);
}
ViewGroup root = activity.findViewById(android.R.id.content);
root.addView(snackbarView);
animateIn();
// Auto dismiss after 3 seconds
handler.postDelayed(this::dismiss, 3000);
}
private void animateIn() {
TranslateAnimation animate = new TranslateAnimation(
0, 0, snackbarView.getHeight(), 0);
animate.setDuration(300);
snackbarView.startAnimation(animate);
snackbarView.setVisibility(View.VISIBLE);
}
public void dismiss() {
TranslateAnimation animate = new TranslateAnimation(
0, 0, 0, snackbarView.getHeight());
animate.setDuration(300);
snackbarView.startAnimation(animate);
snackbarView.setVisibility(View.GONE);
// Remove view after animation
new Handler().postDelayed(() -> {
((ViewGroup) snackbarView.getParent()).removeView(snackbarView);
}, 300);
}
}
view_custom_snackbar.xml
(res/layout)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/snackbar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/black"
android:padding="16dp"
android:orientation="horizontal"
android:gravity="center_vertical"
android:layout_gravity="bottom"
android:elevation="6dp">
<TextView
android:id="@+id/snackbar_text"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:textColor="@android:color/white"
android:text="Message goes here" />
<Button
android:id="@+id/snackbar_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/holo_orange_light"
android:text="OK"
android:background="?attr/selectableItemBackground"
android:minWidth="0dp"
android:minHeight="0dp"
android:padding="8dp"/>
</LinearLayout>
사용 예시
new CustomSnackbar(
MainActivity.this,
"저장되었습니다!",
"실행취소",
v -> {
// 취소 작업 실행
}
);
또는 버튼 없이 단순 메시지만 표시할 수도 있다
new CustomSnackbar(MainActivity.this, "알림 내용입니다", null, null);