I am currently starting an Android app and want to build a list where each item occupies one single row. Right now I only have a TextView but will have more elements later.
I am having problems in placing one element per row since the TextViews just get placed one after the other, like this:
Android View
The layout for this is as follows:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/twitterNumber"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="#dimen/text_margin"
android:textAppearance="?attr/textAppearanceListItem" />
</LinearLayout>
The code for the RecyclerView is:
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/list"
android:name="codehero.twitteralarmclock.ui.main.tweet.TweetsFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
app:layoutManager="LinearLayoutManager"
tools:context=".ui.main.tweet.TweetsFragment"
tools:listitem="#layout/fragment_tweets" />
I've tried changing the orientation of the LinearLayout, adding the maxLines and singleLine attributes to the TextView but nothing changed.
Thanks for you help in advance!
Change the linear layout to a constraint layout like so and re run the code and check
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/twitterNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="#dimen/text_margin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:textAppearance="?attr/textAppearanceListItem" />
</androidx.constraintlayout.widget.ConstraintLayout>
If this still doesn't solve your issue try to get rid of the text appearance attribute and layout margin attribute and recompile.
If it works then you know it's a problem of the attributes
If it still does not solve you might have to paste your adapter code
So it turns the Android Studio default FragmentList code will create a GridLayout if the list count is bigger than 1:
class TweetsFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_tweets_list, container, false)
// Set the adapter
if (view is RecyclerView) {
with(view) {
layoutManager = when {
columnCount <= 1 -> LinearLayoutManager(context)
else -> GridLayoutManager(context, columnCount)
}
adapter = TweetsViewAdapter(TwitterPlaceholderContent.ITEMS)
}
}
return view
}
}
The solution is to always create a LinearLayout inside the onCreateView method:
if (view is RecyclerView) {
with(view) {
layoutManager = LinearLayoutManager(context)
adapter = TweetsViewAdapter(TwitterPlaceholderContent.ITEMS)
}
}
Related
I have a merge layout inside a parent LinearLayout. For some reason, the merge layout is showing up twice, one on top of the other, in the fragment when I run my app. Layout Inspector shows all the elements from the merge layout repeated below the first set, and all directly under the LinearLayout. Even weirder is that the repeated elements have the same id's.
Parent layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<include layout="#layout/child_layout" />
</LinearLayout>
Child layout:
<merge
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="title" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="stuff" />
<Button
android:id="#+id/positiveButton"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<Button
android:id="#+id/negativeButton"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</merge>
Fragment code:
private var _binding: ParentBinding? = null
private val binding get() = _binding!!
private var _contentBinding: ChildBinding? = null
private val contentBinding get() = _contentBinding!!
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
_binding = ParentBinding.inflate(layoutInflater)
_contentBinding = ChildBinding.inflate(layoutInflater, binding.root)
return binding.root
}
What's Happening?
You are inflating the parent layout 2 times in onCreateView.
Solution
Replace this: _contentBinding = ChildBinding.inflate(layoutInflater, binding.root)
With this: _contentBinding = ChildBinding.bind(binding.root)
I learned today that there is something called viewbinding other than databinding.
I was new to viewbinding, but I was already using it in code.
In other words, I knew it was data binding and was using it wrong.
To use viewbinding i need to apply below code in gradle. However, I have never applied anything other than data binding.
dataBinding { enabled = true }
However, in my code, it is accessed through the ID of the view with binding.title.text-like code.
There is also no <data> tag. Isn't this a view binding?
XML
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragment.WritingRoutineFragment">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/Theme.AppBarOverlay"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#color/white"
android:textAppearance="#style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_centerHorizontal="true" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
WRFragment.kt
class WritingRoutineFragment : Fragment() {
var titleArg: String? = null
private var _binding: FragmentWritingRoutineBinding? = null
private val binding get() = _binding!!
private val viewModel = WriteRoutineViewModel()
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
_binding = FragmentWritingRoutineBinding.inflate(inflater, container, false)
viewModel.setTitle(titleArg)
viewModel.title.observe(viewLifecycleOwner) { titleData ->
// UI UPDATE
binding.title.text = titleData // viewbinding? title is TextView ID
}
return binding.root
}
}
viewModel.title.observe(viewLifecycleOwner) { titleData ->
// UI UPDATE
binding.title.text = titleData // viewbinding? title is TextView ID
}
yes,you are using view binding.
if you want to use data binding ,code will be like
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="viewModel"
type="yourpackage.viewmodel" />
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragment.WritingRoutineFragment">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/Theme.AppBarOverlay"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:textColor="#color/white"
android:text="#{viewModel.title}"
android:textAppearance="#style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_centerHorizontal="true" />
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
No, I guess you have to use both. Both provide different functionalities.
I am trying to inflate a view beneath my layout, but my to layout becomes transparent. Any idea why?
Here is my mainActivity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val rootView = (this.findViewById<View>(android.R.id.content) as ViewGroup)
.getChildAt(0) as ViewGroup
val inflater: LayoutInflater = LayoutInflater.from(this)
val view: View = inflater.inflate(R.layout.view_blueberry, rootView, false)
rootView.addView(view)
my activity layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/flContainer">
<RelativeLayout
android:layout_width="match_parent"
android:id="#+id/rlContainer"
android:layout_height="match_parent"
android:background="#ffffff"
tools:ignore="UselessParent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/btStartAnim"
android:text="start"/>
</RelativeLayout>
</RelativeLayout>
and my inflated view:
<?xml version="1.0" encoding="utf-8"?>
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#abab"
/>
This is how it looks: if I add more content it's as transparent as that button. Even weirder considering that I've set a white background in my main layout.
Any idea on how to fix this?
In inflated view you setted transparent background thats why issue ocurres.
<?xml version="1.0" encoding="utf-8"?>
<View
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#abab"
/>
In this please setted proper background without opacity and check once.
android:background="#f00"
Use this code
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val inflater: LayoutInflater = LayoutInflater.from(this)
val view: View = inflater.inflate(R.layout.view_blueberry, rlContainer, false)
rlContainer.addView(view)
}
}
Solution images looks like this
I'm trying to add a fragment within a subclass of DialogFragment. I have a FrameLayout with an id pref_container that I want to replace with a fragment called settingsFragment.
The problem is that fragmentTransaction can't find R.id.pref_container, I don't know if this is because pref_container is only inflated in onCreateView rather than being part of the activity?
I am very new to Android programming, so thank you for your time.
class QuestionnaireDialog : DialogFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
this.setStyle(DialogFragment.STYLE_NORMAL, R.style.AppTheme)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.questionnaire_layout,container,true)
val settingsFragment = ExercisesOnlySettingsFragment()
val ft = fragmentManager?.beginTransaction()
/// **** Here fragmentTransaction can't find R.id.pref_container because it's inflated later and not loaded as part of the activity? ****
ft?.add(R.id.pref_container, settingsFragment)
ft?.commit()
return view
}
}
The runtime error I get is
No view found for id 0x7f09009c (com.lescadeaux.kegel:id/pref_container) for fragment ExercisesOnlySettingsFragment{4ba4b89 #1 id=0x7f09009c}
Here is relavent part of my XML for questionnaire_layout
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/questionnaireLayout"
android:background="#android:color/background_light">
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/scrollView"
app:layout_constraintTop_toBottomOf="#+id/textView5"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="#+id/button"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginEnd="8dp"
app:layout_constraintStart_toStartOf="parent"
android:layout_marginStart="8dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
<!-- RELAVENT PART -->
<FrameLayout
android:layout_width="300dp"
android:layout_height="match_parent"
android:id="#+id/pref_container">
</FrameLayout>
<!-- RELAVENT PART -->
</LinearLayout>
</HorizontalScrollView>
</android.support.constraint.ConstraintLayout>
Please try to use getChildFragmentManager() (or its kotlin equivalent) instead of getFragmentManager() you are using - it will work.
I am currently learning about the Conductor framework for Android and have a bit of a problem or misunderstanding of how it works.
I was under the impression that the method
setRetainViewMode(RetainViewMode.RETAIN_DETACH);
would save the states of the views in the controller. To test the behaviour, I added EditText views, entered a value in it and rotated the screen. I also added 2 views with onclick listeners attached, changing the background color onclick
The result of the test was that the EditText views maintained the state and preserved the entered values. But the 2 views, changed back to their original background color (none).
This is the behaviour of the views regardless on which RetainViewMode is set
I have this simple MainActivity (note: I'm writing in Kotlin):
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var router: Router = Conductor.attachRouter(this, controller_container, savedInstanceState)
if (!router.hasRootController()) {
var t : TestController = TestController()
t.retainViewMode = Controller.RetainViewMode.RETAIN_DETACH
router.setRoot(RouterTransaction.with(t))
}
}
companion object doTask {
fun start(activity : Activity) {
val intent = Intent(activity, MainActivity::class.java)
activity.startActivity(intent)
}
}
}
And here is the the TestController:
class TestController : BaseController() {
var i : Int = 0
var h : Int = 0
override fun onViewBound(view: View) {
view.a.setOnClickListener {
i++
if (i % 2 == 0) {
view.a.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.white))
} else {
view.a.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.turtle_green))
}
}
view.b.setOnClickListener {
h++
if (h % 2 == 0) {
view.b.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.white))
} else {
view.b.setBackgroundColor(ContextCompat.getColor(applicationContext, R.color.blue_light))
}
}
}
override fun inflateView(inflater: LayoutInflater, container: ViewGroup): View {
return inflater.inflate(R.layout.controller_layout_test, container, false)
}
}
And xml layout file controller_layout_test:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<requestFocus></requestFocus>
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="#drawable/logo_simple"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="300dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="30dp"
android:layout_gravity="center"
android:background="#color/transparent50p"
android:padding="20dp">
<EditText
android:id="#+id/gt"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:padding="6dp"
android:background="#color/white_transparent50p"/>
/>
<EditText
android:id="#+id/erergeargf"
android:layout_width="match_parent"
android:layout_height="40dp"
android:layout_gravity="center"
android:layout_marginTop="10dp"
android:padding="6dp"
android:background="#color/white_transparent50p"/>
<View
android:id="#+id/a"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="5dp"
android:layout_gravity="center"></View>
<View
android:id="#+id/b"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_margin="5dp"
android:layout_gravity="center"></View>
</LinearLayout>
</ScrollView>
</FrameLayout>
activity_main xml layout looks like this:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="dk.minreklame.minetilbud_v2.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
/>
</android.support.design.widget.AppBarLayout>
<com.bluelinelabs.conductor.ChangeHandlerFrameLayout
android:id="#+id/controller_container"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
/>
</android.support.design.widget.CoordinatorLayout>
As your views have references to the host Activity, they will never be retained across orientation changes. That would cause a memory leak. The docs on RETAIN_DETACH state:
The Controller will retain its reference to its view when detached, but will still release the reference when a config change occurs.