问题
在附带的屏幕截图中,底部(蓝色)图像下方有一小片橙色。因为图像/布局无法有效地与屏幕底部对齐。
注意:的
在Android Studio
的布局管理器中,展示位置看起来是正确的,我只在实际 Android设备上看到此问题。
问题
我已经尝试设置margin
& padding
完成图像向下移动几个像素,但没有任何变化。如何让这个图像/相对布局与应用程序窗口的底部齐平?
featured.xml(查看)
<?xml version="1.0" encoding="utf-8"?>
<someco.android.phone.Featured xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<RelativeLayout
android:id="@+id/featuredLoaderContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginBottom="52dp"
android:layout_gravity="center_horizontal" />
<someco.android.phone.FeaturedPager
android:id="@+id/featuredPager"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center"
android:layout_marginBottom="52dp" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_centerHorizontal="true" >
<LinearLayout
android:id="@+id/featuredWatchTextContainer"
android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true" >
<TextView
android:id="@+id/featuredDiscoverTxtView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/discover_lakewood"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#FFFFFF"
android:textSize="15sp" />
<TextView
android:id="@+id/featuredWatchTxtView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/watch_announcements"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textColor="#FDC99B"
android:textSize="13sp" />
</LinearLayout>
<ImageView
android:id="@+id/featuredWatchBtn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/featuredWatchTextContainer"
android:layout_marginLeft="20dp"
android:src="@drawable/featured_watch" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/featuredOnlineContainer"
android:layout_width="match_parent"
android:layout_height="62dp"
android:background="@drawable/featured_online_bg"
android:layout_alignParentBottom="true">
<!--android:visibility="gone">-->
<!--x image-->
<ImageView
android:src="@drawable/featured_online_x"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"/>
<!-- in progress text-->
<ImageView
android:src="@drawable/featured_online_in_progress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:layout_marginLeft="82dp"
android:paddingTop="7dp"
/>
<!--join now text-->
<ImageView
android:src="@drawable/featured_online_join_now"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_marginRight="51dp"
android:paddingTop="7dp"/>
<!--join now arrow-->
<ImageView
android:src="@drawable/featured_online_join_arrow"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentRight="true"
android:layout_marginRight="30dp"
android:paddingTop="7dp"/>
</RelativeLayout>
</someco.android.phone.Featured>
Featured.java(控制器)
package someco.android.phone;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Typeface;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import com.crashlytics.android.Crashlytics;
import com.google.android.gms.analytics.HitBuilders;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Date;
//import com.google.analytics.tracking.android.Fields;
//import com.google.analytics.tracking.android.MapBuilder;
public class Featured extends RelativeLayout {
final static String FEATURED_FEED = "/_layouts/Helper.asmx/GetLiveAdSet";
final static String FEATURED_VIDEO_FEED = "/_layouts/PortalAPI.asmx/GetCurrentInfo";
// XML node tags
final static String TAG_ADBIN = "AdBin";
final static String TAG_URL_IMAGE = "MobileAd";
final static String TAG_URL_LINK = "MobileURL";
final static String TAG_RUNNING_TIME = "MobileRunningTime";
final static String TAG_WEEKLY = "Weekly";
final static String TAG_URL_VIDEO = "PreVideoURLNew";
final static int DEFAULT_RUNNING_TIME = 7;
public static Featured instance;
ArrayList<GenericAsyncTask> mTasks = new ArrayList<GenericAsyncTask>();
Date mLastCache;
TextView discoverTxt;
TextView watchTxt;
ImageView watchBtn;
RelativeLayout featuredOnlineChurchContainer;
String videoUrl;
CountDownTimer mTimer;
FeaturedPager mPager;
PagerAdapter mPagerAdapter;
ArrayList<FeaturedTab> mTabs = new ArrayList<FeaturedTab>();
int mSelectedPageIndex = 0;
int mTabLoadedCount = 0;
int mTabCount = 0;
boolean mTabsLoaded = false;
int mPageImageWidth;
int mPageImageHeightMax;
String className;
public Featured(Context context, AttributeSet attrs) {
super(context, attrs);
Featured.instance = this;
this.className = this.getClass().getSimpleName();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
/*
* load the fonts that are going to be used with this view
*/
Typeface museoSans500 = null;
Typeface museoSans700 = null;
if(!this.isInEditMode()) {
MyApplication.instance.mTracker.setScreenName(className);
MyApplication.instance.mTracker.send(new HitBuilders.ScreenViewBuilder().build());
museoSans500 = Typeface.createFromAsset(getContext().getAssets(), "fonts/MuseoSans_500.otf");
museoSans700 = Typeface.createFromAsset(getContext().getAssets(), "fonts/MuseoSans_700.otf");
/*
* start assigning layout elements to local variables
*/
this.discoverTxt = (TextView) this.findViewById(R.id.featuredDiscoverTxtView);
this.watchTxt = (TextView) this.findViewById(R.id.featuredWatchTxtView);
this.watchBtn = (ImageView) this.findViewById(R.id.featuredWatchBtn);
this.mPager = (FeaturedPager) this.findViewById(R.id.featuredPager);
this.featuredOnlineChurchContainer = (RelativeLayout) this.findViewById(R.id.featuredOnlineChurchContainer);
/*
* Assign the fonts
*/
this.discoverTxt.setTypeface(museoSans700);
this.watchTxt.setTypeface(museoSans500);
// calculate tab image width and max height
int paddingPageWidthPixels = MyApplication.dp2px(14 * 2);
int marginPageImageTopPixels = MyApplication.dp2px(42);
int marginPageImageBottomPixels = MyApplication.dp2px(60);
int marginImageShadowMargins = MyApplication.dp2px(25);
// screen width - page margins (14dp x 2)
this.mPageImageWidth = MyApplication.instance.width - paddingPageWidthPixels;
this.mPageImageHeightMax = MyApplication.instance.height - marginPageImageTopPixels - marginPageImageBottomPixels - marginImageShadowMargins;
// Set up pager objects
mPager.setPagingEnabled(false);
mPagerAdapter = new FeaturedPagerLoaderAdapter();
mPager.setAdapter(mPagerAdapter);
mSelectedPageIndex = 1;
mPager.setCurrentItem(mSelectedPageIndex, false);
mPager.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
if(mTimer != null) {
mTimer.cancel();
}
return false;
}
});
// see if online church is playing
final GenericAsyncTask onlineCallback = new GenericAsyncTask();
onlineCallback.callback = new Runnable() {
@Override
public void run() {
try {
Boolean online = (Boolean)onlineCallback.result;
if (online) {
// make the Online Church button visible
featuredOnlineChurchContainer.setVisibility(View.VISIBLE);
}
} catch(Exception e) {
Crashlytics.logException(e);
e.printStackTrace();
}
}
};
GenericAsyncTask onlineTask = OnlineChurch.isOnlineNow(onlineCallback);
mTasks.add(onlineTask);
// doesn't work because the image isn't on the stage just yet
// //set online church bg to fit it's aspect ratio.
// int w = this.featuredOnlineChurchContainer.getWidth();
// int h = ((Double)(w * 0.2)).intValue();
// this.featuredOnlineChurchContainer.getLayoutParams().height = h;
// this.featuredOnlineChurchContainer.requestLayout();
featuredOnlineChurchContainer.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// go to online church
Navigation.instance.onlineChurchBtn.callOnClick();
}
});
mPager.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
@Override
public void onPageSelected(int position) {
super.onPageSelected(position);
mSelectedPageIndex = position;
/*
* We are keeping a duplicate of a tab on either end and then when we get
* to the duplicates we are jumping to the opposite end so the user can
* continue scrolling in either direction in a seemingly infinite fashion.
* -DJL
*/
// if we are at the first left duplicate jump to the
// last tab before right duplication
if(mSelectedPageIndex == 0) {
mSelectedPageIndex = mTabs.size() - 2;
}
// if we are at the first right duplicate jump to
// the tab after the left duplication
if(mSelectedPageIndex == mTabs.size() - 1) {
mSelectedPageIndex = 1;
}
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels);
}
@Override
public void onPageScrollStateChanged(int state) {
super.onPageScrollStateChanged(state);
if (state == ViewPager.SCROLL_STATE_IDLE) {
mPager.setCurrentItem(mSelectedPageIndex, false);
setAdTimer();
}
}
});
this.watchBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
//MyApplication.instance.mGaTracker.sendEvent("ui_action", "button_press", className + ":watch_button", 0L);
// MyApplication.instance.mGaTracker.send(MapBuilder
// .createEvent("ui_action", "button_press", className + ":watch_button", null)
// .build()
// );
MyApplication.instance.mTracker.send(new HitBuilders.EventBuilder()
.setCategory("ui_action")
.setAction("button_press")
.setLabel(className + ":watch_button")
//.setValue()
.build());
if(videoUrl == null) {
getVideoXML(true);
} else {
MyApplication.instance.playVideo(getContext(), videoUrl);
}
}
});
// load last cache date
if(mLastCache == null) {
long time = MyApplication.instance.mSharedPreferences.getLong(MyApplication.PREFS_KEY_FEATURED_LAST_CACHE, 0);
if(time != 0) {
this.mLastCache = new Date(time);
}
}
boolean shouldUseCache = true;
if(mLastCache != null) {
// Get the date for midnight or noon thirty US/Central (Houston, Texas) time. -DJL
Date cacheCheck = Utils.twiceDailyCacheDate();
// use the cache if it is fresh or if there is no network connectivity
shouldUseCache = mLastCache.after(cacheCheck) || !MyApplication.isConnected(getContext());
}
if(shouldUseCache) {
// get the xml from cache if present
String xml = MainActivity.getXmlFromCache(XMLParser.prependHost(Featured.FEATURED_FEED));
if(xml != null) {
this.parseXML(xml);
this.getVideoXML(false);
return;
}
}
if(!MyApplication.isConnected(getContext())){
ContentContainer.instance.noInternetRefresh();
((FeaturedPagerLoaderAdapter) this.mPagerAdapter).loaderOff();
MyApplication.instance.dialogNoCache(getContext());
}
// get the Featured XML
final GenericAsyncTask fxcallback = new GenericAsyncTask();
// fxcallback.background = new Runnable() {
// public void run() {
// task.result = XMLParser.getXMLFromURLPost(XMLParser.prependHost(Featured.FEATURED_FEED));
// }
// };
fxcallback.callback = new Runnable() {
public void run() {
String xml = null;
if(fxcallback.result == null || fxcallback.result.toString().length() == 0) {
MyApplication.instance.dialogServerError(MainActivity.instance);
} else {
xml = fxcallback.result.toString();
MainActivity.addXmlToCache(XMLParser.prependHost(Featured.FEATURED_FEED), xml);
// save cache date
SharedPreferences.Editor editor = MyApplication.instance.mSharedPreferences.edit();
editor.putLong(MyApplication.PREFS_KEY_FEATURED_LAST_CACHE, (new Date()).getTime());
editor.commit();
}
parseXML(xml);
}
};
// task.execute();
GenericAsyncTask fxTask = XMLParser.getXMLFromURLPost(XMLParser.prependHost(Featured.FEATURED_FEED), fxcallback);
mTasks.add(fxTask);
this.getVideoXML(false);
}
}
private void parseXML(final String xml) {
final GenericAsyncTask task = new GenericAsyncTask();
task.background = new Runnable() {
public void run() {
Document doc = XMLParser.getDOM(xml);
if(doc == null) {
return;
}
NodeList featuredNodes = doc.getElementsByTagName(Featured.TAG_ADBIN);
mTabCount = featuredNodes.getLength(); // The number of duplicate tabs we are adding
// loop through all featured nodes <AdBin>
LayoutInflater inflater = LayoutInflater.from(getContext());
for (int i = 0; i < featuredNodes.getLength(); i++) {
Element element = (Element)featuredNodes.item(i);
String imageUrl = XMLParser.getValue(element, Featured.TAG_URL_IMAGE);
String linkUrl = XMLParser.getValue(element, Featured.TAG_URL_LINK);
String time = XMLParser.getValue(element, Featured.TAG_RUNNING_TIME);
int runningTime = Integer.parseInt(time);
if(runningTime <= 0) {
runningTime = Featured.DEFAULT_RUNNING_TIME;
}
// convert to milliseconds
runningTime *= 1000;
URL url = null;
try {
url = new URL(imageUrl);
} catch (MalformedURLException e) {
e.printStackTrace();
}
if(imageUrl != null && imageUrl.length() > 0 && url != null) {
// If we got here then we pulled a new featured XML and need to make
// sure we are using up to date featured images as well -DJL
MainActivity.removeBitmapFromCache(imageUrl);
FeaturedTab tab = (FeaturedTab)inflater.inflate(R.layout.featured_tab, Featured.this, false);
tab.setData(linkUrl, imageUrl, runningTime);
mTabs.add(tab);
} else {
tabFinishedLoading();
}
}
}
};
task.callback = new Runnable() {
public void run() {}
};
task.execute();
mTasks.add(task);
}
public void tabFinishedLoading() {
mTabLoadedCount++;
if(mTabLoadedCount == mTabCount) {
MainActivity.instance.runOnUiThread(new Runnable() {
public void run() {
/*
* We are keeping duplicate of a tab on either end and then when we get
* to the duplicates we are jumping to the opposite end so the user can
* continue scrolling in either direction in a seemingly infinite fashion.
* -DJL
*/
// duplication and insertion for infinite paging
if(mTabs.size() == 1) {
while(mTabs.size() < 3) {
mTabs.add(mTabs.get(0).duplicate());
}
} else {
if(mTabs.size() != 0) {
int last = mTabs.size() - 1;
FeaturedTab posFirst = mTabs.get(last).duplicate();
FeaturedTab posLast = mTabs.get(0).duplicate();
mTabs.add(0, posFirst);
mTabs.add(posLast);
}
}
mPagerAdapter = new FeaturedPagerAdapter(mTabs);
mPager.setAdapter(mPagerAdapter);
mSelectedPageIndex = 1;
mPager.setCurrentItem(mSelectedPageIndex, false);
for(FeaturedTab tab : mTabs) {
if(tab != null) {
tab.clearLoader();
}
}
mTabsLoaded = true;
mPager.setPagingEnabled(true);
setAdTimer();
}
});
}
}
public void removeTab(FeaturedTab tab) {
mTabs.remove(tab);
}
private void getVideoXML(boolean open) {
final boolean shouldOpen = open;
// get the Featured Video XML
String videoXml = MainActivity.getXmlFromCache(XMLParser.prependHost(Featured.FEATURED_VIDEO_FEED));
if(videoXml != null) {
this.parseVideoXML(videoXml, shouldOpen);
return;
}
final GenericAsyncTask task = new GenericAsyncTask();
task.background = new Runnable() {
public void run() {
task.result = XMLParser.getXMLFromURLPost(XMLParser.prependHost(Featured.FEATURED_VIDEO_FEED));
}
};
task.callback = new Runnable() {
public void run() {
String videoXml = null;
if(task.result == null || task.result.toString().length() == 0) {
MyApplication.instance.dialogServerError(MainActivity.instance);
} else {
videoXml = task.result.toString();
MainActivity.addXmlToCache(XMLParser.prependHost(Featured.FEATURED_VIDEO_FEED), videoXml);
}
parseVideoXML(videoXml, shouldOpen);
}
};
task.execute();
mTasks.add(task);
}
private void parseVideoXML(String xml, boolean open) {
Document doc = XMLParser.getDOM(xml);
if(doc != null) {
NodeList nodes = doc.getElementsByTagName(Featured.TAG_WEEKLY);
Element element = (Element)nodes.item(0);
// this.videoUrl = MyApplication.URL_MEDIA + XMLParser.getValue(element, Featured.TAG_URL_VIDEO);
this.videoUrl = XMLParser.getValue(element, Featured.TAG_URL_VIDEO); // for the new url it comes absolute
}
if(open) {
MyApplication.instance.playVideo(this.getContext(), videoUrl);
}
}
private void setAdTimer() {
if(!mTabsLoaded) {
return;
}
if(this.mTimer != null) {
this.mTimer.cancel();
}
if(mTabs.size() < mSelectedPageIndex) {
return;
}
FeaturedTab current = mTabs.get(mSelectedPageIndex);
if(current != null) {
this.mTimer = new CountDownTimer(current.getRunningTime(), 1000) {
public void onTick(long millisUntilFinished) {}
public void onFinish() {
cycle();
}
}.start();
}
}
public void cycle() {
mPager.setCurrentItem(mSelectedPageIndex + 1, true);
this.setAdTimer();
}
private void cancelTasks() {
for(GenericAsyncTask task : mTasks) {
task.cancel(true);
}
mTasks.clear();
}
@Override
protected void onDetachedFromWindow() {
this.cancelTasks();
this.mTabs.clear();
this.mPager.setAdapter(null);
this.mPagerAdapter = null;
if(this.mTimer != null) {
this.mTimer.cancel();
}
Featured.instance = null;
super.onDetachedFromWindow();
};
static public class FeaturedPagerAdapter extends PagerAdapter {
ArrayList<FeaturedTab> mPages;
public FeaturedPagerAdapter(ArrayList<FeaturedTab> pages) {
super();
mPages = pages;
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
FeaturedTab tab = mPages.get(position);
if(tab != null) {
container.addView(tab);
}
return tab;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
@Override
public int getCount() {
return mPages.size();
}
@Override
public boolean isViewFromObject(View view, Object object) {
return (view == object);
}
}
static public class FeaturedPagerLoaderAdapter extends PagerAdapter {
FeaturedTab mLoadPage;
public FeaturedPagerLoaderAdapter() {
super();
LayoutInflater inflater = LayoutInflater.from(Featured.instance.getContext());
mLoadPage = (FeaturedTab)inflater.inflate(R.layout.featured_tab, Featured.instance, false);
}
@Override
public Object instantiateItem(ViewGroup container, int position) {
container.addView(mLoadPage);
return mLoadPage;
}
@Override
public void destroyItem(ViewGroup container, int position, Object object) {
container.removeView((View)object);
}
@Override
public int getCount() {
return 1;
}
@Override
public boolean isViewFromObject(View view, Object object) {
return (view == object);
}
public void loaderOff() {
mLoadPage.mProgressBar.setVisibility(View.INVISIBLE);
}
}
}
设备屏幕截图
抽拉/ featured_online_bg
答案 0 :(得分:0)
这个问题的新细节引出了我的答案。我本来会关闭这个问题,但是我发现了一些有用的东西,所以为了记录起见,我将把它们留在这里。
根据我的情况,当设置layout_alignParentBottom
时,marginTop
不适用于任何值。我必须使用带有负值的layout_marginBottom
。
我发现在此布局之外有许多父容器,并且此布局已加载到那里。我不得不对其中的一些进行测试clipChildren="false"
,但我终于找到了导致问题的原因。所以这是剪辑儿童和布局边距的组合导致了我的问题。
希望有人能从我失去的时间中获益