Не удается запустить ShowcaseView из фрагмента в Android
Я использую последнюю версию ShowcaseView отсюда
Когда я пытался использовать его с демонстрационным приложением и активностью, он работает, но когда я пытался использовать его с фрагментами, он ломал мое приложение без ошибок logcat
@override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
final ImageButton saveButton = (ImageButton) view.findViewById(R.id.button_Save);
ViewTarget viewTarget = new ViewTarget(saveButton);
showcaseView = new ShowcaseView.Builder(getActivity())
.setTarget(viewTarget)
.setContentTitle("Reset Button")
.setContentText("This will erase all the data in the table except the line that in progress")
.build();
}
в чем может быть проблема здесь?
EDIT1:
когда я пытался сделать то же самое в своей фрагментации и не работал, но когда я делал то же самое, но придерживался мнения, которое выражалось в фрагментации, а не во фрагменте, это работало.
EDIT2: мне удалось получить ошибку от LogCat.
FATAL EXCEPTION: main
java.lang.IllegalArgumentException: width and height must be > 0
at android.graphics.Bitmap.createBitmap(Bitmap.java:724)
at android.graphics.Bitmap.createBitmap(Bitmap.java:703)
at android.graphics.Bitmap.createBitmap(Bitmap.java:670)
at com.github.amlcurran.showcaseview.ShowcaseView.updateBitmap(ShowcaseView.java:169)
at com.github.amlcurran.showcaseview.ShowcaseView.onGlobalLayout(ShowcaseView.java:343)
at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:839)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2050)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1249)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:6364)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:791)
at android.view.Choreographer.doCallbacks(Choreographer.java:591)
at android.view.Choreographer.doFrame(Choreographer.java:561)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:777)
at android.os.Handler.handleCallback(Handler.java:730)
at android.os.Handler.dispatchMessage(Handler.java:92)
at android.os.Looper.loop(Looper.java:176)
at android.app.ActivityThread.main(ActivityThread.java:5419)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:525)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1046)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:862)
at dalvik.system.NativeStart.main(Native Method)
Я понимаю ошибку, но в чем причина?
5 ответов
На мой взгляд, лучшее решение - поместить код в обратный вызов представления "post", таким образом он будет выполняться, когда все будет отображено.
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.post(new Runnable() {
@Override
public void run() {
// your showcase initialisation code here
showcase();
}
});
}
Я проверил это и работал как шарм.
Похоже, это проблема времени. Если вызов происходит до того, как представления фактически прорисованы на экране, он не может сказать, что ширина и высота должны быть> 0. Это ясно из исходного кода ShowcaseView
, в updateBitmap()
функция, где она не работает:
bitmapBuffer = Bitmap.createBitmap(getMeasuredWidth(), getMeasuredHeight(), Bitmap.Config.ARGB_8888);
где getMeasuredWidth()
а также getMeasuredHeight()
будет ноль, прежде чем на самом деле рисование.
Обойти эту проблему можно с хорошей задержкой. В настоящее время я использую его с задержкой 5 с и звоню из onResume()
с помощью Handler
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
new ShowcaseView.Builder(mActivity)
.setTarget(new ViewTarget(saveMatchButton))
.setContentTitle("This is a demo")
.build();
}
}, 5000);
Я думаю, что лучшее решение - это изменить класс ShowcaseView (функция updateBitmap ()). Передача любых ненулевых значений измерения в функцию createBitmap (), когда getMeasure * возвращает 0, гарантирует, что растровое изображение создано с допустимым размером. Так как функция updateBitmap () будет вызвана снова, как только представление получит фактический размер, это не представляет проблемы.
private void updateBitmap()
{
if (bitmapBuffer == null || haveBoundsChanged())
{
if(bitmapBuffer != null)
bitmapBuffer.recycle();
bitmapBuffer = Bitmap.createBitmap(getMeasuredWidth() > 0 ? getMeasuredWidth() : 1,
getMeasuredHeight() > 0 ? getMeasuredHeight() : 1,
Bitmap.Config.ARGB_8888);
}
}
Согласно этому ответу, ShowcaseView
должен быть построен в onCreateView()
,
Даже если вы можете записать это в RESUME() и убедиться, что если вы пишете в резюме, вы должны поддерживать флаг, который будет использоваться, чтобы избежать проблемы создания более одной витрины выше при каждом вызове OnResume(),
if (!mShowCaseVisible){
ActionViewTarget target = new ActionViewTarget(getActivity(), ActionViewTarget.Type.HOME);
showcaseView = new ShowcaseView.Builder(getActivity(), true)
.setTarget(target)
.setContentTitle("Home Screen")
.setOnClickListener(this)
.setContentText("Click on top left corner to seemenu.")
.doNotBlockTouches()`enter code here`
.hideOnTouchOutside()
.singleShot(42)
.build();
showcaseView.setButtonText("Next");
mShowCaseVisible = true;
}