I want the height of the bottom dialog to expend to match_parent (as empty activity)
Here is my code.
MainActivity
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button OpenBottomSheet = findViewById(R.id.open_bottom_sheet);
OpenBottomSheet.setOnClickListener(
new View.OnClickListener() {
#Override
public void onClick(View v)
{
BottomSheetDialog bottomSheet = new BottomSheetDialog();
bottomSheet.show(getSupportFragmentManager(),
"ModalBottomSheet");
}
});
}
}
BottomSheetDialog
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.Toast;
import androidx.annotation.Nullable;
import com.google.android.material.bottomsheet.BottomSheetBehavior;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
public class BottomSheetDialog extends BottomSheetDialogFragment {
#Override
public View onCreateView(LayoutInflater inflater, #Nullable
ViewGroup container, #Nullable Bundle savedInstanceState)
{
View v = inflater.inflate(R.layout.buttom_sheet_layout,
container, false);
return v;
}
}
Here is full code.
To have full screen BottomSheetDialogFragment, you'd:
Use a full screen window using android:windowFullscreen applied in a custom style by overriding getTheme()
Use STATE_EXPANDED state of the bottom sheet to expand to the entire dialog window
Set the bottomSheet layout to MATCH_PARENT
Optionally disable the bottom sheet STATE_HALF_EXPANDED using behavior.setSkipCollapsed()
Applying that to your class:
public class BottomSheetDialog extends BottomSheetDialogFragment {
#Override
public View onCreateView(LayoutInflater inflater, #Nullable
ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.bottom_sheet_layout,
container, false);
Button algo_button = v.findViewById(R.id.algo_button);
Button course_button = v.findViewById(R.id.course_button);
algo_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getActivity(),
"Algorithm Shared", Toast.LENGTH_SHORT)
.show();
dismiss();
}
});
course_button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getActivity(),
"Course Shared", Toast.LENGTH_SHORT)
.show();
dismiss();
}
});
return v;
}
#Override
public int getTheme() {
return R.style.dialog_style;
}
#Override
public void onStart() {
super.onStart();
View bottomSheet =
((com.google.android.material.bottomsheet.BottomSheetDialog)
getDialog()).findViewById(com.google.android.material.R.id.design_bottom_sheet);
if (bottomSheet != null) {
// set the bottom sheet state to Expanded to expand to the entire window
BottomSheetBehavior.from(bottomSheet).setState(BottomSheetBehavior.STATE_EXPANDED);
// disable the STATE_HALF_EXPANDED state
BottomSheetBehavior.from(bottomSheet).setSkipCollapsed(true);
// set the bottom sheet height to match_parent
ViewGroup.LayoutParams layoutParams = bottomSheet.getLayoutParams();
layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
bottomSheet.setLayoutParams(layoutParams);
}
// Make the dialog cover the status bar
getDialog().getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
}
}
And this is the custom style for full screen:
<resources>
<style name="dialog_style" parent="Theme.MaterialComponents.BottomSheetDialog">
<item name="android:windowFullscreen">true</item>
</style>
</resources>
If you're using Material3, then extend the style from Theme.Material3.DayNight.BottomSheetDialog instead.
Edit:
Great ! but the top bar does not hide
You can make the bottom sheet hide the status bar by setting the below window flag; the above class is updated with that:
// Make the dialog cover the status bar
getDialog().getWindow().setFlags(
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS,
WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
Also for devices with notch you need to set android:windowLayoutInDisplayCutoutMode to the style:
<resources>
<style name="dialog_style" parent="Theme.MaterialComponents.BottomSheetDialog">
<item name="android:windowFullscreen">true</item>
<item name="android:windowLayoutInDisplayCutoutMode">shortEdges</item>
</style>
</resources>
There is a tricky way, useful for me, to modify behavior and set window height to achieve the full-screen effect.
class BottomSheetDialog : BottomSheetDialogFragment() {
private var mBehavior: BottomSheetBehavior<View>? = null
override fun onStart() {
super.onStart()
mBehavior?.state = BottomSheetBehavior.STATE_EXPANDED
mBehavior?.peekHeight = 0
}
fun onCreateView(
inflater: LayoutInflater,
#Nullable container: ViewGroup?,
#Nullable savedInstanceState: Bundle?
): View {
//content in buttom_sheet_layout should be match_parent
return inflater.inflate(
R.layout.buttom_sheet_layout,
container, false
)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setupBottomSheetFullScreen(view)
}
private fun setupBottomSheetFullScreen(view: View) {
view.updateLayoutParams {
height = getWindowHeight()
}
mBehavior = BottomSheetBehavior.from(view.parent as View)
}
private fun getWindowHeight(): Int {
return Resources.getSystem().displayMetrics.heightPixels
}
}
You can set the height of the BottomSheetDialog to match_parent by adding the following line of code to your BottomSheetDialog class:
#Override
public void onStart() {
super.onStart();
View view = getView();
if (view != null) {
ViewGroup.LayoutParams params = view.getLayoutParams();
params.height = ViewGroup.LayoutParams.MATCH_PARENT;
view.setLayoutParams(params);
}
}
This code overrides the onStart() method, which is called when the BottomSheetDialog is shown. It gets the view of the BottomSheetDialog, sets the height of the view to
Alternatively, you can use this line in your onCreateView method:
getDialog().getWindow().setLayout(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
This line sets the width and height of the bottom sheet dialog to match_parent, making it take up the full screen.
Related
I'm trying android for 2 weeks and I run into some problem.
I have a list of burger that I fetch from my database, I made a view with that.
The app needs to increment and decrement the quantity that the user wants so that he can pass an order.
enter image description here
My problem is that when I click on the + button, the app update multiple text view and I don't understand where that came from and how to fix it.
here is a sample of my code for the view :
package com.example.fastfood;
import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import java.util.ArrayList;
public class BurgerAdapter extends ArrayAdapter<Burger> {
public BurgerAdapter(Context context, ArrayList<Burger> burgers) {
super(context, 0,burgers);
}
#NonNull
#Override
public View getView(final int position, #Nullable View convertView, #NonNull ViewGroup parent) {
final Burger burger = getItem(position);
if(convertView == null){
convertView = LayoutInflater.from(getContext()).inflate(R.layout.item_burger, parent, false);
}
TextView burgerName = (TextView) convertView.findViewById(R.id.burgerName);
TextView burgerPrice = (TextView) convertView.findViewById(R.id.burgerPrice);
TextView qteCmd = (TextView) convertView.findViewById(R.id.qteCmd);
Button btnMoins = (Button) convertView.findViewById(R.id.moins);
Button btnPlus = (Button) convertView.findViewById(R.id.plus);
btnMoins.setTag(qteCmd);
btnPlus.setTag(qteCmd);
btnMoins.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView qteCmd = (TextView) v.getTag();
burger.changeQuantitee(-1);
Log.d("DEBUG", "Button moins : "+burger.getNom()+" "+burger.getQuantitee()+" "+qteCmd.getId());
qteCmd.setText(Integer.toString(burger.getQuantitee()));
}
});
btnPlus.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView qteCmd = (TextView) v.getTag();
burger.changeQuantitee(1);
Log.d("DEBUG", "Button moins : "+burger.getNom()+" "+burger.getQuantitee()+" "+qteCmd.getId());
qteCmd.setText(Integer.toString(burger.getQuantitee()));
}
});
burgerName.setText(burger.getNom());
burgerPrice.setText(burger.getPrix()+"€");
return convertView;
}
}
You are not supposed to alter views of an AdapterView (ListView, Spinner, RecyclerView etc) inside methods that do not belong to Adapter classes (as you do with your onClick() method overrides. What you should be doing is after you mutate your data model notify the Adapter with .notifyDataSetChanged().
Also I strongly suggest that you do not capture the local variable Burger inside the onClick(). Rather, fetch the data from burgersList with adapter position. So your code be like:
btnMoins.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
TextView qteCmd = (TextView) v.getTag();
Burger b = getItem(position);
b.changeQuantitee(-1);
Log.d("DEBUG", "Button moins : "+burger.getNom()+" "+burger.getQuantitee()+" "+qteCmd.getId());
//Instead of changing the state of the TextView yourself, you should instead do following:
BurgerAdapter.this.notifyDataSetChanged();
}
});
I am developing an app on android studio (Java)
I have created a tabLayout that swipes between two fragments using a viewPager2 + Fragment State Adapter
On fragment02, I have animated a textView.
When I swipe onto the fragment the animation plays on loop, as it should.
However, if I swipe off the fragment and then back on, the animation is no longer playing.
I really need the animation to continue.
Any advice is welcome and I thank you in advance :)
Please be aware I am rather new to this.
// MainActivity.java
package Zoomie.App;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;
import android.os.Bundle;
import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getSupportActionBar().setTitle("Welcome");
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
ViewPager2 viewPager2 = findViewById(R.id.ViewPager);
viewPager2.setAdapter(new FragmentPagerAdapter(this));
TabLayout tabLayout = findViewById(R.id.tabLayout);
TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(
tabLayout, viewPager2, new TabLayoutMediator.TabConfigurationStrategy() {
#Override
public void onConfigureTab(#NonNull TabLayout.Tab tab, int position) {
switch (position) {
case 0: {
tab.setText("Fragment 01");
break;
}
default: {
tab.setText("Fragment 02");
break;
}
}
}
});
tabLayoutMediator.attach();
}
}
// FragmentPagerAdapter.java
package Zoomie.App;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import androidx.viewpager2.adapter.FragmentStateAdapter;
public class FragmentPagerAdapter extends FragmentStateAdapter {
public FragmentPagerAdapter(#NonNull FragmentActivity fragmentActivity) {
super(fragmentActivity);
}
#NonNull
#Override
public Fragment createFragment(int position) {
switch (position){
case 0:
return new Fragment01();
default:
return new Fragment02();
}
}
#Override
public int getItemCount() {
return 2;
}
}
// Fragment02.java
package Zoomie.App;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.TextView;
public class Fragment02 extends Fragment {
public Fragment02() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView=inflater.inflate(R.layout.fragment_02, container, false);
TextView fade01 = (TextView) rootView.findViewById(R.id.textView_fade_01);
Animation fade = AnimationUtils.loadAnimation(getActivity(), R.anim.fade_animation);
fade01.startAnimation(fade);
return rootView;
}
}
// fade_animation.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<alpha
android:fromAlpha="0"
android:toAlpha=".5"
android:duration="375"
android:repeatMode="reverse"
android:repeatCount="infinite"
/>
</set>
You should move the loading animation method into onResume() lifecycle method. In your case:
private TextView fade01;
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState
) {
View rootView = inflater.inflate(R.layout.fragment_02, container, false);
fade01 = (TextView) rootView.findViewById(R.id.textView_fade_01);
return rootView;
}
#Override
public void onResume() {
super.onResume();
Animation fade = AnimationUtils.loadAnimation(getActivity(), R.anim.fade_animation);
fade01.startAnimation(fade);
}
I have a custom base image adapter in which i use for a grid view.. i was trying to use it for a full image view but i get an error while creating the my custom adapter object...passing the parameters....
By the way sorry for any mistakes ,this is my first question:
This is for gallery to preview full
Fullimage java:Where the error is present
public class FullImage extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_full_image);
Intent i= getIntent();
int position = i.getExtras().getInt("id");
MyPicsAdapter adapter = new MyPicsAdapter(this);//ERROR is here!!!
ImageView imageView = (ImageView) findViewById(R.id.fullimage);
imageView.setImageResource(adapter.item_image[position]);
}
}
Custom Adapter:
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;
public class MyPicsAdapter extends BaseAdapter{
public Context context;
public final int item_image[];
public final String item_text[];
public MyPicsAdapter(Context context, int item_image[], String[] item_text)
{
this.context = context;
this.item_image = item_image;
this.item_text = item_text;
}
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View gridView;
if (convertView == null) {
gridView = new View(context);
// get layout from custom_gridview.xml
gridView = inflater.inflate(R.layout.custom_gridview, null);
// set value into imageview
ImageView image = (ImageView) gridView.findViewById(R.id.item_image);
image.setImageResource(item_image[position]);
// set value into textview
TextView text = (TextView) gridView.findViewById(R.id.item_text);
text.setText(item_text[position]);
} else {
gridView = (View) convertView;
}
return gridView;
}
#Override
public int getCount() {
return item_text.length;
}
#Override
public Object getItem(int position) {
return null;
}
#Override
public long getItemId(int position) {
return 0;
}
}
Class where the images are:
import android.content.Intent;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.Toast;
public class Smitpic extends AppCompatActivity {
GridView grid;
String text[] = {
"Colorful countryside on the way to Smit",
"A local bus at the Village square",
"Cattle grazing at the village ground",
"Pine trees lined the road leading to the Wooden house",
"Traditional wooden house made without any metal nails",
"Intricate carvings on the door of the wooden house",
"A cottage at Smit",
"Villagers returning home after washing clothes at the river",
"A river idlying flowing near Smit",
"Colorful field of grasses and pine trees"
};
int image[] = {R.drawable.smit1,R.drawable.smit2,
R.drawable.smit3,R.drawable.smit4,R.drawable.smit5,
R.drawable.smit6,R.drawable.smit7,R.drawable.smit8,
R.drawable.smit9,R.drawable.smit10
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_smitpic);
grid = (GridView)findViewById(R.id.simpleGrid);
grid.setAdapter(new MyPicsAdapter(this,image,text));
// grid.setAdapter(new ImageAdapter(this,image));
grid.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(getApplicationContext(),text[position],Toast.LENGTH_LONG).show();
Intent a= new Intent(getApplicationContext(),FullImage.class);
a.putExtra("id",position);
startActivity(a);
}
});
}
}
You can pass only the image as follows,
grid.setOnItemClickListener(new AdapterView.OnItemClickListener(){
#Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(getApplicationContext(),text[position],Toast.LENGTH_LONG).show();
Intent a = new Intent(getApplicationContext(),FullImage.class);
a.putExtra("id", image[position]);
startActivity(a);
}
});
Inside onCreate of FullImage Activity get the image and display it in the ImageView,
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_full_image);
int image = getIntent().getExtras().getInt("id");
ImageView imageView = (ImageView) findViewById(R.id.fullimage);
imageView.setImageResource(image);
}
I have a Sliding Tab layout with a Recycler View that holds three tabs. The first tab holds a Card View. What is the best way to achieve the following: when you click on a button inside the first card the Card View (a fragment) it is replaced by another Card View (another fragment) while staying in the same tab.
I know I have to create a new fragment and use transaction. I just do not know where. Do I put it in an adapter? If possible I would like to use onClick for the button in the XML Layout for the first Card View.
Sliding Tab How-to
Sliding Tab Layout
Sliding Tab Strip
MainActivity:
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.view.ViewPager;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
public class MainActivity extends AppCompatActivity {
private Toolbar toolbar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.app_bar);
setSupportActionBar(toolbar);
// Get the ViewPager and set it's PagerAdapter so that it can display items
ViewPager viewPager = (ViewPager) findViewById(R.id.viewpager);
viewPager.setAdapter(new MainFragmentPageAdapterForTabs(getSupportFragmentManager(),
MainActivity.this));
// Give the SlidingTabLayout the ViewPager
SlidingTabLayout slidingTabLayout = (SlidingTabLayout) findViewById(R.id.sliding_tabs);
// Center the tabs in the layout
slidingTabLayout.setDistributeEvenly(true);
slidingTabLayout.setViewPager(viewPager);
}
public void bodyButtonAction(View view){
Intent intentBody = new Intent(MainActivity.this, MainActivity.class);
startActivity(intentBody);
}
Tab Fragments:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
// In this case, the fragment displays simple text based on the page
public class MainPageFragmentForTabs extends Fragment {
public static final String ARG_PAGE = "ARG_PAGE";
private int mPage;
public static MainPageFragmentForTabs newInstance(int page) {
Bundle args = new Bundle();
args.putInt(ARG_PAGE, page);
MainPageFragmentForTabs fragment = new MainPageFragmentForTabs();
//body fragment
Fragment bodyFragment = new MainPageFragmentForTabs();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mPage = getArguments().getInt(ARG_PAGE);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = null;
if(mPage==1){
View view1 = inflater.inflate(R.layout.main_fragment_page, container, false);
FragmentActivity a = getActivity();
//recycler
RecyclerView recyclerView = (RecyclerView) view1.findViewById(R.id.my_recycler_view);
recyclerView.setHasFixedSize(true);
//layout manager
LinearLayoutManager manager = new LinearLayoutManager(a);
manager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
MainAdapterCV1 ca = new MainAdapterCV1();
recyclerView.setAdapter(ca);
view=view1;
}
if(mPage==2){
View view2 = inflater.inflate(R.layout.main_fragment_page, container, false);
//stand-in code
TextView textView = (TextView) view2;
textView.setText("Fragment #" + mPage);
//stand-in code
view=view2;
}
if(mPage==3){
View view3 = inflater.inflate(R.layout.main_fragment_page, container, false);
//stand-in code
TextView textView = (TextView) view3;
textView.setText("Fragment #" + mPage);
//stand-in code
view=view3;
}
return view;
}
}
Adapter for first Card View:
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class MainAdapterCV1 extends RecyclerView.Adapter<MainAdapterCV1.CardViewHolder> {
//
#Override
public int getItemCount() {
return 1;
}
#Override
public void onBindViewHolder(CardViewHolder cardViewHolder, int i) {
}
//
#Override
public CardViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View itemView = LayoutInflater.
from(viewGroup.getContext()).
inflate(R.layout.main_card_view1, viewGroup, false);
return new CardViewHolder(itemView);
}
public static class CardViewHolder extends RecyclerView.ViewHolder {
public CardViewHolder(View v) {
super(v);
}
}
}
Technically you can add or replace a fragment anywhere you want. However doing it in the Adapter, I think, is more logical than inside a fragment. One reason is the adapter normally has the click event listener code, and I don't see it in the posted code, which is fine. Sample code to replace a fragment (using your code):
MainPageFragmentForTabs myFragment = MainPageFragmentForTabs.newInstance(page);
FragmentTransaction transaction = thisActivity.getFragmentManager().beginTransaction();
// Replace the Fragment (set in MainActivity) with this fragment.
transaction.replace(R.id.sample_content_fragment, myFragment, "MainPage");
transaction.addToBackStack(null); // support the Back key
transaction.commit();
Just need to know the proper way to implement Google analytics to track when a user is on a fragment in real time this is what is do now
#Override
public void onResume() {
super.onResume();
Tracker myTracker = parentActivity.getTracker();
myTracker.setCustomMetric(1, (long) 1);
myTracker.sendView("Music View");
}
the getTracker class is in my main activity and just returns the instance of tracker in the main activity
Any help would be much appreciated!
Mochini's answer uses Google Analytics V2. Bellow you can see how to do it on V4 and V3:
V4:
Application:
public class YourApplication extends Application
{
public synchronized Tracker getTracker() {
try {
final GoogleAnalytics googleAnalytics = GoogleAnalytics.getInstance(this);
return googleAnalytics.newTracker(R.xml.analytics);
}catch(final Exception e){
Log.e(TAG, "Failed to initialize Google Analytics V4");
}
return null;
}
}
res/xml/analytics.xml (you can name it anything, it does not need to be called "analytics")
<?xml version="1.0" encoding="utf-8" ?>
<resources xmlns:tools="http://schemas.android.com/tools" tools:ignore="TypographyDashes">
<!--Replace placeholder ID with your tracking ID-->
<string name="ga_trackingId">UA-XXXXXXXX-X</string>
<!--Enable automatic activity tracking-->
<bool name="ga_autoActivityTracking">true</bool>
<!--Disable automatic exception tracking-->
<bool name="ga_reportUncaughtExceptions">false</bool>
</resources>
build.gradle:
compile 'com.google.android.gms:play-services:7.3.0'
Fragment superclass:
public abstract class TrackedFragment extends Fragment{
#Override
public void onResume() {
super.onResume();
final Tracker tracker = yourApplicationInstance.getTracker();
if(tracker != null){
tracker.setScreenName(getClass().getSimpleName());
tracker.send(new HitBuilders.ScreenViewBuilder().build());
}
}
}
V3
import android.os.Bundle;
import android.support.v4.app.Fragment;
import com.google.analytics.tracking.android.EasyTracker;
import com.google.analytics.tracking.android.Fields;
import com.google.analytics.tracking.android.MapBuilder;
import com.google.analytics.tracking.android.Tracker;
public abstract class TrackedFragment extends Fragment{
private Tracker tracker;
#Override
public void onActivityCreated(final Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
this.tracker = EasyTracker.getInstance(getActivity());
}
#Override
public void onResume() {
super.onResume();
this.tracker.set(Fields.SCREEN_NAME, getClass().getSimpleName());
this.tracker.send( MapBuilder.createAppView().build() );
}
}
Source: https://developers.google.com/analytics/devguides/collection/android/v3/migration
This an example using FragmentActivity and fragments:
Create XML file in value folder (values/analytics.xml):
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- Replace placeholder ID with your tracking ID -->
<string name="ga_trackingId">XX-xxxxxxxx-x</string>
<!-- Enable Activity tracking -->
<bool name="ga_autoActivityTracking">true</bool>
<!-- Enable debug -->
<bool name="ga_debug">true</bool>
<!-- The screen names that will appear in your reporting -->
<string name="com.example.myapp.FragmentActivity">Fragment activity</string>
<!--
The inverval of time after all the collected data
should be sent to the server, in seconds.
-->
<integer name="ga_dispatchPeriod">20</integer>
</resources>
In your FragmentActivity class, add this:
#Override
protected void onStart() {
super.onStart();
EasyTracker.getInstance().setContext(this.getBaseContext());
EasyTracker.getInstance().activityStart(this); // Add this method
}
#Override
protected void onStop() {
super.onStop();
EasyTracker.getInstance().activityStop(this); // Add this method
}
Create new class in your package: TrackedFragment.java
public class TrackedFragment extends Fragment {
private Tracker tracker;
private String activityId;
private String fragmentId;
#Override
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
EasyTracker.getInstance().setContext(getActivity().getApplicationContext());
this.tracker = EasyTracker.getTracker();
this.fragmentId = getClass().getSimpleName();
this.activityId = getActivity().getClass().getSimpleName();
}
#Override
public void onResume() {
super.onResume();
this.tracker.sendView("/" + this.activityId + "/" + this.fragmentId);
}
}
Finally, your fragment should extend from TrackedFragment like:
public class NewFragment extends TrackedFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.newfragment, null);
}
}
Tracking methods section suggests that you just need to call EasyTracker.getInstance().setContext(getActivity()); first, then you can use the tracker in "other classes".
manual screen tracking section suggests that you can track a Fragment view with myTracker.sendView("Home Screen");
Another approach for V3 (since onResume() is tied to the Activity and not the Fragment. This works well when the parent/child relationships are well-known.
Parent Fragment sends initial event onStart():
public class ParentFragment extends Fragment {
private Tracker mTracker;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mTracker = EasyTracker.getInstance(getActivity());
}
#Override
public void onStart() {
super.onStart();
mTracker.set(Fields.SCREEN_NAME, "Parent Fragment");
mTracker.send(MapBuilder.createAppView().build());
}
}
Child Fragment overrides both onStart() and onStop() :
public class ChildFragment extends Fragment {
private Tracker mTracker;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mTracker = EasyTracker.getInstance(getActivity());
}
#Override
public void onStart() {
super.onStart();
mTracker.set(Fields.SCREEN_NAME, "Child Fragment");
mTracker.send(MapBuilder.createAppView().build());
}
#Override
public void onStop() {
super.onStop();
mTracker.set(Fields.SCREEN_NAME, "Parent Fragment");
mTracker.send(MapBuilder.createAppView().build());
}
}
Tiago's version can't be used in the new goole analytics v4. Instead, use this code from Google's docs
package com.google.android.apps.mobileplayground;
import com.google.android.apps.mobileplayground.AnalyticsSampleApp.TrackerName;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
/**
* Class to exercise Event hits.
*/
public class EventFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View view = inflater.inflate(R.layout.event, container, false);
setupEvent(view, R.id.video1Play, R.string.videoCategory, R.string.videoPlay, R.string.video1);
setupEvent(view, R.id.video1Pause, R.string.videoCategory, R.string.videoPause,
R.string.video1);
setupEvent(view, R.id.video2Play, R.string.videoCategory, R.string.videoPlay, R.string.video2);
setupEvent(view, R.id.video2Pause, R.string.videoCategory, R.string.videoPause,
R.string.video2);
setupEvent(view, R.id.book1View, R.string.bookCategory, R.string.bookView, R.string.book1);
setupEvent(view, R.id.book1Share, R.string.bookCategory, R.string.bookShare, R.string.book1);
final Button dispatchButton = (Button) view.findViewById(R.id.eventDispatch);
dispatchButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Manually start a dispatch (Unnecessary if the tracker has a dispatch interval)
GoogleAnalytics.getInstance(getActivity().getApplicationContext()).dispatchLocalHits();
}
});
return view;
}
private void setupEvent(View v, int buttonId, final int categoryId, final int actionId,
final int labelId) {
final Button pageviewButton = (Button) v.findViewById(buttonId);
pageviewButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// Get tracker.
Tracker t = ((AnalyticsSampleApp) getActivity().getApplication()).getTracker(
TrackerName.APP_TRACKER);
// Build and send an Event.
t.send(new HitBuilders.EventBuilder()
.setCategory(getString(categoryId))
.setAction(getString(actionId))
.setLabel(getString(labelId))
.build());
}
});
}
}
with android google analytics v4
i tried this and it worked
refering this https://developers.google.com/analytics/devguides/collection/android/v4/events
import java.net.URLEncoder;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Xml.Encoding;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.widget.ScrollView;
import android.widget.TextView;
import com.Blog.gkgyan.AnalyticsSampleApp.TrackerName;
import com.Blog.gkgyan.parser.RSSFeed;
import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;
import com.google.android.gms.analytics.GoogleAnalytics;
import com.google.android.gms.analytics.HitBuilders;
import com.google.android.gms.analytics.Tracker;
public class DetailFragment extends Fragment {
private int fPos;
RSSFeed fFeed;
String country;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fFeed = (RSSFeed)getArguments().getSerializable("feed");
fPos = getArguments().getInt("pos");
Tracker t = ((AnalyticsSampleApp) getActivity().getApplication()).getTracker(
TrackerName.APP_TRACKER);
// Build and send an Event.
t.send(new HitBuilders.EventBuilder()
.setCategory(fFeed.getItem(fPos).getTitle())
.setAction("viewpager click")
.setLabel("viewpager label")
.build());
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.detail_fragment, container, false);
// Initializr views
TextView title = (TextView)view.findViewById(R.id.title);
WebView desc = (WebView)view.findViewById(R.id.desc);
// Enable the vertical fading edge (by default it is disabled)
ScrollView sv = (ScrollView)view.findViewById(R.id.sv);
sv.setVerticalFadingEdgeEnabled(true);
// Set the views
desc.getSettings().setJavaScriptEnabled(true);
title.setText(fFeed.getItem(fPos).getTitle());
country = "<html xmlns=\"http://www.w3.org/1999/xhtml\"><head><meta http-equiv=\"Content-Type\" content=\"text/html; charset=iso-8859-1\"><style type=\"text/css\">p{text-align:justify;font-size:125%;}</style></head><body>" + "<p>" + fFeed.getItem(fPos).getDescription()+"</p>"+"</body></html>";
//desc.loadData( country, "text/html", "UTF-8");
//desc.loadData( country, "text/html; charset=utf-8", "utf-8");
desc.loadData( URLEncoder.encode(country).replaceAll("\\+", " "), "text/html", Encoding.UTF_8.toString());
return view;
}
}