Replies: 3 comments 6 replies
-
There's a relatively new You need to set the AndroidFragment<EpubNavigatorFragment> { fragment ->
} I don't know if this was ever used in production with a Readium powered app. Please let us know how it goes for you. |
Beta Was this translation helpful? Give feedback.
-
If anyone needs an explanation on how to implement a Navigator within Jetpack Compose, bump me here I'll explain |
Beta Was this translation helpful? Give feedback.
-
@mickael-menu Here's my personal Jetpack Compose approach. Hope it helps those who are stuck implementing Readium in a Jetpack Compose (or a Compose multiplatform app): 1- Make sure you have kotlin {
...
buildFeatures {
viewBinding = true
}
} 2- In order to make fragments compatible with Jetpack Compose, we need to make use of this library: 3- Create a small XML layout file, name it "epub_fragment_parent.xml" or something like that. It will host the actual <?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navigator"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000">
</androidx.fragment.app.FragmentContainerView> 4- Now let's dive into the actual Jetpack Compose code. This is the parent composable everything will happen here): lateinit var ebookFragmentParentBinding: EbookFragmentParentBinding <-- important
lateinit var sfm: FragmentManager <-- //VERY important, make sure to initialize this from your activity's onCreate, also, make sure your activity is a subclass of `FragmentActivity` because otherwise it won't have a FragmentManager.
lateinit var epubnav: EpubNavigatorFragment <-- //VERY important, this is the actual fragment provided by Readium
var publication: Publication? = null
@Composable
actual fun EbookViewport(book: Book) {
val context = LocalContext.current
var pub by remember { mutableStateOf<Publication?>(null) }
LaunchedEffect(null) {
viewmodel.viewModelScope.launch(Dispatchers.IO) {
pub = getEPUB() <-- //Some kind of logic that gets an EPUB publication from a link for example
}
}
if (pub == null) {
Surface(modifier = Modifier.fillMaxSize()) {
Box(contentAlignment = Alignment.Center) {
CircularProgressIndicator() <-- //Just an example of how we show a loading indicator if a publication is not loaded yet
}
}
}
pub?.let {
EbookHost(pub!!) <-- //This will be visible once a publication is ready.
}
} Now let's look at the AndroidViewBinding(
modifier = Modifier.fillMaxSize().systemBarsPadding(),
factory = { inflater, viewgroup, attachToParentBoolean ->
ebookFragmentParentBinding = EbookFragmentParentBinding.inflate(inflater, viewgroup, attachToParentBoolean)
return@factory ebookFragmentParentBinding
}
) {
publication = pub <-- //publication is a global property because it has to be used by EpubFragment as well
/* This is a "onUpdate" lambda which runs very often (on every recomposition)
Therefore, we need a boolean to make sure it runs only once just to insert our EpubNavigatorFragment */
val nav = ebookFragmentParentBinding.navigator
if (nav.getFragment<EpubFragment>() == null) { <-- //it will show a warning that it's always true, it's a false positive so IGNORE IT
if (pub.conformsTo(Publication.Profile.PDF)) {
sfm.beginTransaction()
.replace(ebookFragmentParentBinding.navigator.id, PdfFragment::class.java, Bundle(), "Pdf")
.commitNow()
} else if (pub.conformsTo(Publication.Profile.EPUB)) {
sfm.beginTransaction()
.replace(ebookFragmentParentBinding.navigator.id, EpubFragment::class.java, Bundle(), "Epub")
.commitNow()
}
}
} Last but not least, we need the actual fragment code for @OptIn(ExperimentalReadiumApi::class)
class EpubFragment : Fragment(), EpubNavigatorFragment.Listener {
override fun onCreate(savedInstanceState: Bundle?) {
/** The only way to create an EpubNavigatorFragment is via this factory */
childFragmentManager.fragmentFactory = EpubNavigatorFactory(
publication = publication!!, <-- this assumes publication is always initialized at this point, which should be no issue
configuration = EpubNavigatorFactory.Configuration(
defaults = EpubDefaults(scroll = true)
)
).createFragmentFactory(
initialLocator = null,
listener = this,
configuration = EpubNavigatorFragment.Configuration { }
)
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View {
val binding = EbookFragmentParentBinding.inflate(inflater, container, false)
if (savedInstanceState == null) {
/** Adding the [EpubNavigatorFragment] to the parent host fragment */
childFragmentManager.beginTransaction()
.add(R.id.navigator, EpubNavigatorFragment::class.java, Bundle(), "EpubNavigator")
.commitNow()
}
/** We have obtained our EPUB navigator by now */
epubnav = childFragmentManager.findFragmentByTag("EpubNavigator") as EpubNavigatorFragment
return binding.root
}
} That's it. You should be all set. You can add decorations, input listeners and anything you like, within "EpubFragment" as it's the best way to add your initialization code. But you can also do it via "LaunchedEffect()" in Jetpack Compose, I think there are multiple ways to doing this. |
Beta Was this translation helpful? Give feedback.
-
i want to use this library but reading guides and getting nowhere ,views in compose documentation ain't helping much either. i do have ideas of views it would be great help if somebody gives a brief overview how i could better understand interop part
Beta Was this translation helpful? Give feedback.
All reactions