I know this question asked many times. But I didn't get the asnswer. I have a layout which contains two Tabs. When I switch the tabs they are reloading every time. Many people suggest to use viewpager. But I know nothing about viewPager. My project is almost completed.
My layout code:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_devices);
mPreferences = getSharedPreferences(PreferencesManager.SCOPE,
MODE_PRIVATE);
mBundle = getIntent().getExtras();
try {
rl = (RelativeLayout) findViewById(R.id.mainLayout);
//((Object) rl).setOffscreenPageLimit(2);
fragMentTra = getFragmentManager().beginTransaction();
ActionBar bar = getActionBar();
bar.addTab(bar.newTab().setText(TAB_NAME1).setTabListener(this));
bar.addTab(bar.newTab().setText(TAB_NAME2).setTabListener(this));
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
bar.show();
} catch (Exception e) {
e.getMessage();
}
}
FragmentMine fram1;
FragmentTransaction fragMentTra = null;
FragmentAll fram2;
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
if (tab.getText().equals(TAB_NAME1)) {
try {
rl.removeAllViews();
} catch (Exception e) {
}
fram1 = new FragmentMine();
fragMentTra.addToBackStack(null);
fragMentTra = getFragmentManager().beginTransaction();
fragMentTra.add(rl.getId(), fram1);
fragMentTra.commit();
} else if (tab.getText().equals(TAB_NAME2)) {
try {
rl.removeAllViews();
} catch (Exception e) {
}
fram2 = new FragmentAll();
fragMentTra.addToBackStack(null);
fragMentTra = getFragmentManager().beginTransaction();
fragMentTra.add(rl.getId(), fram2);
fragMentTra.commit();
}
}
How to stop reloading tabs when switched?
Every time onTabSelected() is called you are instantiating a new Fragment.
You should make use of a FragmentPagerAdapter.
Check: http://developer.android.com/reference/android/support/v4/app/FragmentPagerAdapter.html
As pointed out by Rik, you are instantiating a new Fragment by:
fram1 = new FragmentMine();
You need to save the state of your fragment by overriding onSaveInstanceState():
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// Whatever is there to save
}
Using this class helps you for work onViewCreated only one time
abstract class BaseFragment<B : ViewDataBinding>(
private val viewID: Int,
) : Fragment() {
var dataBinding: B? = null
private var isPageLoaded = false
abstract fun onViewCreated()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if (!isPageLoaded) {
onViewCreated()
isPageLoaded = true
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?,
): View? {
if (dataBinding == null) {
performDataBinding(inflater, container)
}
return dataBinding?.root
}
private fun performDataBinding(
inflater: LayoutInflater,
container: ViewGroup?,
) {
dataBinding = DataBindingUtil.inflate(
inflater, viewID, container, false
)
dataBinding?.let {
it.lifecycleOwner = this
}
}
}
and use this class in your fragment like this :
class HomeFragment : BaseFragment<FragmentHomeBinding>(R.layout.fragment_home) {
...
}
Related
This is my Activity function that the startActivity(i) unable go to the Fragment page after scanning a barcode, I have try that it can go to activity page and show the code successful but I need it go to Fragment page.
barcodeDetector.setProcessor(object : Detector.Processor<Barcode> {
override fun release() {
Toast.makeText(applicationContext, "Scanner has been closed", Toast.LENGTH_SHORT)
.show()
}
override fun receiveDetections(detections: Detector.Detections<Barcode>) {
val barcodes = detections.detectedItems
if (barcodes.size() == 1) {
scannedValue = barcodes.valueAt(0).rawValue
runOnUiThread {
cameraSource.stop()
Toast.makeText(this#InsertStockInActivity, scannedValue, Toast.LENGTH_SHORT).show()
val i = Intent(this#InsertStockInActivity, comFragment::class.java)
.putExtra("cameraSource", scannedValue)
startActivity(i)
finish()
}
}else
{
Toast.makeText(this#InsertStockInActivity, "value- else", Toast.LENGTH_SHORT).show()
}
}
})
This is my Fragment page. do I write anything wrong?
class comFragment : Fragment() {
private lateinit var binding: FragmentComBinding
private val nav by lazy { findNavController() }
private val vm: StockInViewModel by activityViewModels()
private val formatter = SimpleDateFormat("dd MMMM yyyy '-' hh:mm:ss a", Locale.getDefault())
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = FragmentComBinding.inflate(inflater, container, false)
//binding.btnScanBarcode.setOnClickListener{ nav.navigate(R.id.insertStockInActivity) }
val value = requireActivity().intent.getStringExtra("cameraSource")
binding.edtId.findViewById<EditText>(R.id.value)
return binding.root
}
}
You use intent to navigate to Activity not to Fragment.
You can:
Use fragment transactions to navigate to new Fragment in your current Activity
Use intent to navigate to new Activity and in your new Activity use fragment transactions to your Fragment
You need to create a class that extends FragmentActivity and start your fragment there:
public class MyFragmentActivity extends YourActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null){
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, new MyFragment ()).commit();}
}
}
then fragment constructor:
public MyFragment() {
}
then from your calling activity, start your fragment activity in the normal way
Intent i = new Intent(YourActivity.this, MyFragment.class);
startActivity(i);
I have an old project written in Java made to slide horizontally between three different fragments, and they were stated on the main activity. It worked perfect.
public class MainActivity extends AppCompatActivity {
ViewPager pager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
pager = findViewById(R.id.theViewPagerInTheMainActivity);
pager.setOffscreenPageLimit(3);
PagerAdapter adapter = new PagerAdapter(getSupportFragmentManager());
Frag_A aFrag = new Frag_A();
adapter.addItem(aFrag);
Frag_B bFrag = new Frag_B();
adapter.addItem(bFrag);
Frag_C cFrag = new Frag_C();
adapter.addItem(cFrag);
pager.setAdapter(adapter);
pager.setCurrentItem(1);
}
class PagerAdapter extends FragmentStatePagerAdapter {
ArrayList<Fragment> items = new ArrayList<Fragment>();
public PagerAdapter(FragmentManager fragManager) {
super(fragManager);
}
public void addItem(Fragment item) {
items.add(item);
}
#Override
public Fragment getItem(int position) {
return items.get(position);
}
#Override
public int getCount() {
return items.size();
}
}
}
Each three different fragments, followed by different codes that works fine, starts with:
public class Frag_A extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.frag_a, container, false);
}
}
Same goes like Frag_B and frag_b layout, Frag_C and frag_c layout.
I'm doing it all again now, but in Kotlin and using ViewPager2. I'm not making any fragments programmatically, I want all three of their ID written on the main activity. Converting the code directly in Android Studio didn't work, of course, and hours of searching failed to cover my needs. From my humble understanding, creating different-coloured fragments from one pre-made fragment doesn't really seem to be adaptable for my project and all ended up in red underlines. Can it be made without importing .v4 supporting library?
Solved by myself!
class MainActivity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val adapter = DatPagerAdapter(this)
val fragsList = listOf(Frag_A(), Frag_B(), Frag_C())
adapter.fragsListHere.addAll(fragsList)
theVP2InTheMainActivity.adapter = adapter
theVP2InTheMainActivity.setCurrentItem(1)
}
class DatPagerAdapter(fragactivity: FragmentActivity) : FragmentStateAdapter(fragactivity) {
val fragsListHere = mutableListOf<Fragment>()
override fun getItemCount():Int = fragListHere.size
override fun createFragment(position: Int): Fragment = fragListHere[position]
}
}
Since FragmentPagerAdapter is depecrated. I switch it to FragmentStateAdapter(FragmentActivity fragmentActivity) that work fine with ViewPager2
My ViewPagerAdapter :
```
class ViewPagerAdapter(fragmentActivity: FragmentActivity):
FragmentStateAdapter(fragmentActivity){
override fun getItemCount(): Int {
return 3
}
override fun createFragment(position: Int): Fragment {
return when(position){
0 -> {
HomeFragment()
}
1 -> {
HomeFragment2()
}
2 -> {
HomeFragment3()
}
else -> {
throw Resources.NotFoundException("Position Not Found")
}
}
}
}
```
My Activity :
class MainActivity5 : AppCompatActivity() {
private lateinit var binding: ActivityMain5Binding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMain5Binding.inflate(layoutInflater)
val view = binding.root
setContentView(view)
binding.viewpager.adapter = ViewPagerAdapter(this)
}
// If the user is currently looking at the first step, allow the system to handle the
// Back button. This calls finish() on this activity and pops the back stack.
override fun onBackPressed() {
if (binding.viewpager.currentItem == 0) {
super.onBackPressed()
} else {
// Otherwise, select the previous step.
binding.viewpager.currentItem = binding.viewpager.currentItem - 1
}
}
}
Thanks For asking!!
Note: I used binding to replace findViewById
I want to switch Fragments by button click inside the fragment itself. I have have created viewpager + tablayout, everything worked until i made an interface to communicate with my activity where i can set different Page for Viewpager. I am pretty new to Kotlin and I do not understand why I am catching NullPointer :(
I probably initialized my button poorly too, but I am not sure
Here is my MainActivity code
class MainActivity : AppCompatActivity()
, MyFragment1.buttonClick {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
tablayout.addTab(tablayout.newTab().setIcon(R.drawable.ic_fragment1))
tablayout.addTab(tablayout.newTab().setIcon(R.drawable.ic_fragment_2))
tablayout.addTab(tablayout.newTab().setIcon(R.drawable.ic_fragment3))
tablayout.tabGravity = TabLayout.GRAVITY_FILL
val adapter = MyAdapter(this, supportFragmentManager, tablayout.tabCount)
viewpager!!.adapter = adapter
viewpager.addOnPageChangeListener(TabLayout.TabLayoutOnPageChangeListener(tablayout))
val tabStrip = tablayout.getChildAt(0) as LinearLayout
for (i in 0 until tabStrip.childCount) {
tabStrip.getChildAt(i).setOnTouchListener { v, event -> true }
}
}
override fun buttonClicked(view: View) {
viewpager.currentItem = 2
}
fun selectIndex(index: Int) {
viewpager.currentItem = index
}
override fun onBackPressed() {
val currentPos = viewpager.currentItem
if (currentPos != 0) {
viewpager.currentItem = 0
} else {
super.onBackPressed()
}
}
}
**And a code for my Fragment**
class MyFragment1 : Fragment() {
private var click: buttonClick? = null
interface buttonClick {
fun buttonClicked(view: View)
}
var currentPage: Int = 0
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.fragment_my_fragment1, container, false)
val btn: Button = view?.findViewById(R.id.btn_next)
btn.setOnClickListener {
Toast.makeText(context, "test", Toast.LENGTH_LONG).show()
click?.buttonClicked(it)
}
return view
}
}
Have you initialized your click variable?
private var click: buttonClick? = null
override fun onAttach(context: Context?) {
super.onAttach(context)
click = context as? buttonClick
}
override fun onDetach() {
super.onDetach()
click = null
}
This this code :
in which i open bottomSheetDialogFragment in that i want to add fragment.
I want to add multiple fragments in bottomsheetDialogFragment but it throws
java.lang.IllegalArgumentException: No view found for id 0x7f0a01cb
class AddNotesBottomSheetDialog : BottomSheetDialogFragment() {
private lateinit var bottomSheetDialog: BottomSheetDialog
private lateinit var bottomSheetBehavior: BottomSheetBehavior<View>
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
Log.v(LOG_TAG, "-> onCreateDialog")
bottomSheetDialog = BottomSheetDialog(context!!)
var view = View.inflate(context, R.layout.bottom_sheet_notes, null)
bindViews(view)
bottomSheetDialog.setContentView(view)
bottomSheetBehavior = BottomSheetBehavior.from(view.parent as View)
bottomSheetBehavior.isHideable = false
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
return bottomSheetDialog
}
private fun bindViews(view: View) {
loadAddNotesFragments()
}
override fun onStart() {
super.onStart()
Log.v(LOG_TAG, "-> onStart")
bottomSheetBehavior.isHideable = false
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
if (!visible)
dialog.hide()
}
fun show(fragmentManager: FragmentManager) {
Log.v(LOG_TAG, "-> show")
visible = true
if (isAdded) {
Log.v(LOG_TAG, "-> Is already added")
dialog.show()
} else {
Log.v(LOG_TAG, "-> Not added")
show(fragmentManager, AddNotesBottomSheetDialog.LOG_TAG)
}
}
override fun onDestroyView() {
super.onDestroyView()
Log.v(LOG_TAG, "-> onDestroyView")
}
private fun loadAddNotesFragments() {
val createNoteFragment = CreateNoteFragment()
val ft = fragmentManager?.beginTransaction()
ft?.replace(R.id.placeHolderBottomSheet, createNoteFragment)
ft?.commit()
}
}
Solved: I tried to add multiple fragments transaction in bottomSheetDialogFragment but it's not possible to do transaction in BottomSheetDialogFragment thats why its through this exception. so i used viewPager inside the BottomsheetDialog and its work perfect.
try calling loadAddNotesFragments() in
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
loadAddNotesFragments()
}
And try using childFragmentManager to begin transaction's: reference
private fun loadAddNotesFragments() {
val createNoteFragment = CreateNoteFragment()
val ft = childFragmentManager()?.beginTransaction()
ft?.replace(R.id.placeHolderBottomSheet, createNoteFragment)
ft?.commit()
}
I believe this will solve your problem.
UPDATE:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.bottom_sheet_notes, container, false)
}
use this to inflate the content of bottomSheet, and
REMOVE:
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
Log.v(LOG_TAG, "-> onCreateDialog")
bottomSheetDialog = BottomSheetDialog(context!!)
var view = View.inflate(context, R.layout.bottom_sheet_notes, null)
bindViews(view)
bottomSheetDialog.setContentView(view)
bottomSheetBehavior = BottomSheetBehavior.from(view.parent as View)
bottomSheetBehavior.isHideable = false
bottomSheetBehavior.state = BottomSheetBehavior.STATE_EXPANDED
return bottomSheetDialog
}
ADD:
override fun onStart() {
super.onStart()
val bottomSheet = dialog.findViewById(android.support.design.R.id.design_bottom_sheet) as ViewGroup //FrameLayout as my
val mBehavior = BottomSheetBehavior.from(bottomSheet)
//Add Behavior logic here
}
NOTE:
No need of overriding onCreateDialog() unless you want your own Dialog to be initiated, i.e some other type of dialog.
This is ActivityUtil code
public class ActivityUtil {
public static void addFragmentToActivity(#NonNull FragmentManager fragmentManager,
#NonNull Fragment fragment, int frameId, String fragmentTag) {
//Fragment fragment1=fragmentManager.findFragmentByTag(fragmentTag);
Preconditions.checkNotNull(fragmentManager);
Preconditions.checkNotNull(fragment);
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.replace(frameId, fragment, fragmentTag);
transaction.addToBackStack(fragmentTag);
transaction.commit();
}
}
This is main fragment class here i want add /replace fragment transaction by using kotlin fragment i.e,CameraFragment Class
I Already used Photofragment class now i want change the kotlin fragment, how do achieve this scenario?
public class ExpLotBcodeFragment extends Fragment{
public ExpLotBcodeFragment() {
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setRetainInstance(true);
}
#SuppressLint("ClickableViewAccessibility")
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_exp_lot_bcode, container, false);
}
#OnClick({R.id.scan})
public void onClick(View view) {
switch (view.getId()) {
case R.id.scan:
//here i want to add kotlin call CameraFragment class
//This is old call
ActivityUtil.addFragmentToActivity(getFragmentManager(),
photoFragment, R.id.frame_content, "photoFragment");
break;
}
This is kotlin class
class CameraFragment : Fragment() {
companion object {
fun newInstance(): CameraFragment {
return CameraFragment ()
}
}
override fun onCreateView(inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_exp_lot_bcode, container, false)
}
}
From CameraFragment class i want to add/replace to ExpLotBcodeFragment class ?
Either you have to call CameraFragment.Companion.newInstance() or directly call new CameraFragment() to get instance of CameraFragment.
Check below:
#OnClick({R.id.scan})
public void onClick(View view) {
switch (view.getId()) {
case R.id.scan:
CameraFragment cameraFragment = CameraFragment.Companion.newInstance();
ActivityUtil.addFragmentToActivity(getFragmentManager(),
cameraFragment, R.id.frame_content, "cameraFragment");
break;
}
}