I'm using ActionBarSherlock. On Android 3.0+, there's a subtle shadow under the ActionBar. But not on older Androids.
The problem is, that the shadow set in android:windowContentOverlay appears at the top of the Activity Window. And on older Androids, ActionBar is part of the Window. So the shadow overlays the top of the ActionBar, instead of overlaying the content under ActionBar.
Is there a way to solve this, without manually inserting the shadow to every Activity's layout?
As #wingman hinted in the comments, I overrided setContentView(...) in my base Activity like this:
#Override
public void setContentView(int layoutResId) {
View view = getLayoutInflater().inflate(layoutResId, null);
setContentView(view);
}
#Override
public void setContentView(View view) {
int wrapContent = ViewGroup.LayoutParams.WRAP_CONTENT;
int matchParent = ViewGroup.LayoutParams.MATCH_PARENT;
view.setLayoutParams(new ViewGroup.LayoutParams(matchParent, matchParent));
View shadow = new View(this);
shadow.setBackgroundResource(R.drawable.action_bar_shadow);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(matchParent,
wrapContent);
shadow.setLayoutParams(params);
RelativeLayout container = new RelativeLayout(this);
container.addView(view);
container.addView(shadow);
super.setContentView(container);
}
Related
I'm following the Cheesesquare example project to understand the new design material library.
I'm wondering if there's a way to use a custom view (like Telegram) with ImageView, title and subtitle instead of the simple Title provided by CollapsingToolbarLayout widget.
Thanks.
I had the same problem and spend many hours trying to find a solution. My solution was to add the collapsing Views (ImageView and TextView) inside the CollapsingToolbarLayout and then handle the transition in code. This way it's more flexible and simpler than extending from CollapsingToolbarLayout.
First you'll need to add your Views inside the CollapsingToolbarLayout with the parallax properties:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop:"80dp"
android:src="#drawable/icon"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.8"/> //set vertical transition here
Then set the scaling of the Views with the help of an OnOffsetchangeListner:
private static final float SCALE_MINIMUM=0.5f;
appBarLayout.setOnWorkingOffsetChange(new ControllableAppBarLayout.OnWorkingOffsetChange() {
#Override
public void onOffsetChange(int offSet, float collapseDistance) {
imageView.setScaleX(1 + (collapseDistance * SCALE_MINIMUM));
imageView.setScaleY(1 + (collapseDistance * SCALE_MINIMUM));
textView.setScaleX(1 + (collapseDistance * SCALE_MINIMUM));
textView.setScaleY(1 + (collapseDistance * SCALE_MINIMUM));
// You can also setTransitionY/X, setAlpha, setColor etc.
}
});
Somehow the default offsetChangedListener didn't work properly for me (you probably still should try it with the default listener first), so I used the ControllableAppBarLayout from https://gist.github.com/blipinsk/3f8fb37209de6d3eea99 and added the following:
private OnWorkingOffsetChange onWorkingOffsetChange;
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
if (!isInEditMode()) {
onWorkingOffsetChange.onOffsetChange(i, (float) i / appBarLayout.getTotalScrollRange());
}
}
public void setOnWorkingOffsetChange(OnWorkingOffsetChange listener) {
this.onWorkingOffsetChange = listener;
}
public interface OnWorkingOffsetChange {
void onOffsetChange(int offSet, float collapseDistance);
}
The only problem is, that you would need to set
app:contentScrim="#00000000" (transparent)
for your CollapsingToolbarLayout, so your views are still visible when the toolbar is collapsed. If you really need the collapsing-background effect I'm sure you could "fake" this by setting the alpha of a background ImageView in the OffsetChangeListener. ;)
From the widget itself there doesn't seem to be a way to enable this directly, like it was possible to add custom views to the Toolbar.
What you could try to do however, is open the source of the CollapsingToolbarLayout.class and check out how the CollapsingTextHelper.class is used to have the title set. You could try to make your own widget by extending from the the CollapsingToolbarLayout.
These links can help you out with creating custom components/views, if you haven't created them before:
Custom Views, Custom Components
I haven't tried this yet, but it's actually something I was thinking about trying to achieve a similar solution as you are looking for. Steps I tihkn I would follow, so far:
Create custom attributes for subtitle settings in attrs.xml
Create your own MyCollapsingToolbarLayout by extending the original one.
Make sure to call super in the constructors, so the original component will stay intact.
Create a subtitleTextHelper by adding a new CollapsingTextHelper to your component.
Override onDraw to actually draw your subtitle.
Update the layout containing your CollapingsToolbarLayout with your subtitle attributes (default styling and such, maybe a fixed subtitle Text).
Apply the changes in the Activity containing your CollapsingToolbar. (Convert CollapsingToolbarlayout to MyCollapingsToolbarLayout, set subtitles, extra custom settings, etc).
Cross fingers, test.
Going to have a look at it now.
Going to edit Christopher's answer slightly to show how you can get your custom view to not disappear on collapse:
First you'll need to add your Views inside the CollapsingToolbarLayout with the parallax properties:
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop:"80dp"
android:src="#drawable/icon"
app:layout_collapseMode="parallax"
app:layout_collapseParallaxMultiplier="0.8"/> //set vertical transition here
Instead add the custom view's programmatically and it won't disappear on collapse. For example here is a view that contains a title and a subtitle:
final FrameLayout frameLayout = new FrameLayout(mActivity);
FrameLayout.LayoutParams frameLayoutParams = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
frameLayout.setLayoutParams(frameLayoutParams);
// Create new LinearLayout
final LinearLayout linearLayout = new LinearLayout(mActivity);
frameLayoutParams =new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, dpToPixels(78));
frameLayoutParams.gravity = Gravity.BOTTOM;
linearLayout.setLayoutParams(frameLayoutParams);
linearLayout.setOrientation(LinearLayout.VERTICAL);
// Add textviews
final TextView textView1 = new TextView(mActivity);
LinearLayout.LayoutParams linearLayoutParams =new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
frameLayoutParams.gravity = Gravity.BOTTOM;
textView1.setLayoutParams(linearLayoutParams);
textView1.setText("Title");
textView1.setTextColor(ContextCompat.getColor(mActivity, R.color.colorWhite));
textView1.setTextSize(TypedValue.COMPLEX_UNIT_SP, 40);
linearLayout.addView(textView1);
final TextView textView2 = new TextView(mActivity);
linearLayoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
textView2.setLayoutParams(linearLayoutParams);
textView2.setText("Subtitle");
textView2.setTextColor(ContextCompat.getColor(mActivity, R.color.colorWhite));
textView2.setTextSize(TypedValue.COMPLEX_UNIT_SP, 20);
linearLayout.addView(textView2);
frameLayout.addView(linearLayout);
collapsingToolbar.addView(frameLayout);
final float SCALE_MIN=0.4f;
AppBarLayout appBarLayout = (AppBarLayout) mActivity.findViewById(R.id.appBarLayout);
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
#Override
public void onOffsetChanged(AppBarLayout appBarLayout, int offSet) {
float collapsedRatio = (float) offSet / appBarLayout.getTotalScrollRange();
linearLayout.setScaleX(1 + (collapsedRatio * SCALE_MIN));
linearLayout.setScaleY(1 + (collapsedRatio * SCALE_MIN));
FrameLayout.LayoutParams frameLayoutParams =new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, dpToPixels(78));
frameLayoutParams.gravity = Gravity.BOTTOM;
frameLayoutParams.setMargins(Math.round(dpToPixels(48) * (1+collapsedRatio)), 0, 0, Math.round(dpToPixels(15) * collapsedRatio));
linearLayout.setLayoutParams(frameLayoutParams);
// You can also setTransitionY/X, setAlpha, setColor etc.
}
});
/////
float lastCollapsedRatio = -2;
////
private int dpToPixels(int padding_in_dp){
final float scale = getResources().getDisplayMetrics().density;
int padding_in_px = (int) (padding_in_dp * scale + 0.5f);
return padding_in_px;
}
My app is finished and done. Now the client wants to have a demo functionality. So when running in Demo mode there must be on every screen the tekst "DEMO" in the left corner.
How can I do this by changing minimum of code?
I see solutions with relative layouts and stuff but then I need to change every activity layout?
Thanks to pskink I've found the solution.
In my baseActivity I override my setContentView like this:
#Override
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
if(config.isInDemoMode()) {
RelativeLayout lContainerLayout = new RelativeLayout(this);
lContainerLayout.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
TextView mCustomView = new TextView(this);
mCustomView.setText("DEMO");
RelativeLayout.LayoutParams lTextViewParams = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
lTextViewParams.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
mCustomView.setLayoutParams(lTextViewParams);
lContainerLayout.addView(mCustomView);
addContentView(lContainerLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
}
}
I want to add a scroll view to all the layouts that I have. But dynamically. Because the app will run in different screen sizes, and when I will get a screen size smaller than a specific size, then I want to show the layout in a scroll view.
So I made this method, it will be called on the check that the screen is small. I will pass my activity and I want to change the root layout to scroll view or just add a ScrollView as the root layout. So if the root layout is a LinearLayout, then I want to put that layout in the ScrollView. And I have not named all the layouts, meaning that I didn't give an ID to the layout, so I cannot use findViewById.
public static void SetActivityRoot(Activity c) {
View v = c.getWindow().getDecorView();
// View v = v.getRootView();
ScrollView sv = new ScrollView(c);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
sv.setLayoutParams(lp);
((ViewGroup)v.getParent()).removeView(v);
sv.addView((View) v);
((ViewGroup)v.getParent()).addView(sv);
}
It's giving me an error saying that "you cannot remove view from null" etc. Or that "you cannot add view to layout as it already has parent view". How can I make this work?
Finally solved my problem.
public static void SetActivityRoot(Activity c) {
View v = ((ViewGroup)c.findViewById(android.R.id.content)).getChildAt(0);
ScrollView sv = new ScrollView(c);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
sv.setLayoutParams(lp);
((ViewGroup) v.getParent()).removeAllViews();
sv.addView((View) v);
c.addContentView(sv, lp);
}
I'm trying to show a simple prompt dialog fragment programatically. I've extended SherlockFragmentActivity, and coded a custom SherlockDialogFragment implementation in it.
(I'm using ActionbarSherlock library, but I think the problem doesn't have to do with it, and would also be observed using regular ActionBar and Fragments).
This is the overriden oncreateDialog method inside the custom FragmentActivity class:
#Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
//Skipped section. Setting positive, negative buttons, title, message on builder.
efPsw = new EditText(getActivity());
// Hacky margin stuff (yeah, I know it's dirty)
DisplayMetrics displaymetrics = new DisplayMetrics();
getActivity().getWindowManager().getDefaultDisplay().getMetrics(displaymetrics);
int smallestSide = Math.min(displaymetrics.heightPixels, displaymetrics.widthPixels);
int margin = (int)(0.5 * ((double) smallestSide));
efPsw.setTransformationMethod(PasswordTransformationMethod.getInstance());
FrameLayout fl = new FrameLayout(getActivity());
FrameLayout.LayoutParams layoutParams = new FrameLayout.LayoutParams(
FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.WRAP_CONTENT);
layoutParams.setMargins(margin, 0, margin, 0);
fl.addView(efPsw, layoutParams);
builder.setView(fl);
return builder.create();
}
When I show the fragment, the inner framelayout is not shown (looks like an invisible rectangle that grows in height with each typed character). I'm not sure what am I doing wrong here, but certainly something must be wrong, since if I put the edittext in setView everything works fine (but without margin).
Thanks in advance.
it looks like you are making the margin half the smallest side, which in portrait mode would make your margin the entire screen. i would start by reducing the margin.
you can apply the margins directly to the EditText using the setLayoutParams() method. That would eliminate the FrameLayout.
I have a question regarding Android Activitys:
An Activity has the Method addContentView(View) while a ViewGroup has a (similar?) addView(View) Method.
Unfortunately its undocumented where the View from addContentView is placed. Is it like a LinearLayout just adding the View to the bottom, or is it more like a FrameLayout, which adds its Views "onTop" ? Does it depend on the ViewGroup set by setContentView?
If I dive into the sources I see that addContentView will call Window's abstract Method addContentView. Unfortunately I cannot see which class is implementing this Method. So whats the behaviour of Activitys addContentView exactly?
The base layout of every activity is a FrameLayout. This means the layout you usually set via setContentView() is a child of this layout. addContentView() adds just another child, therefore it behaves like a FrameLayout (which means it adds new UI elements above existing ones).
You can check this by using a tool called hierachyviewer from your ANDROID_SDK\tools folder. Here are two screenshots:
This is the layout before calling addContentView(), my activity consists of the default FrameLayout, holding a LinearLayout with a Button (my layout here). This is reflected in the bottom row here, the other elements above are the title/statusbar.
After adding a TextView via addContentView() it looks like this. You can see that the base FrameLayout got a new child.
public void addContentView(View view,
LayoutParams params) {
mActivity.addContentView(view, params);
}
//
public static void SetActivityRoot(Activity c) {
View v = ((ViewGroup)c.findViewById(android.R.id.content)).getChildAt(0);
ScrollView sv = new ScrollView(c);
LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT,
LayoutParams.MATCH_PARENT);
sv.setLayoutParams(lp);
((ViewGroup) v.getParent()).removeAllViews();
sv.addView((View) v);
c.addContentView(sv, lp);
}
//
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LinearLayout mainLayout =
(LinearLayout)findViewById(R.id.mainlayout);
//newButton added to the existing layout
Button newButton = new Button(this);
newButton.setText("Hello");
mainLayout.addView(newButton);
//anotherLayout and anotherButton added
//using addContentView()
LinearLayout anotherLayout = new LinearLayout(this);
LinearLayout.LayoutParams linearLayoutParams =
new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
Button anotherButton = new Button(this);
anotherButton.setText("I'm another button");
anotherLayout.addView(anotherButton);
addContentView(anotherLayout, linearLayoutParams);
}
}