Ich befolge den "Entwicklung Android Apps" -Kurs in Udacity und wenn ich die App (Sonnenschein App für Wetter) in einem Handgerät-Emulator, wann Ich klicke auf einen Gegenstand aus der Vorhersage-Liste, er muss die Details des ausgewählten Gegenstandes zeigen, aber die App stürzt ab und es zeigt den folgenden Fehler: "ScrollView kann nur ein direktes Kind hosten" Sie können sehen, dass mein Bild nur ein Kind hat"ScrollView kann nur ein direktes Kind hosten" sogar mit einem einzigen linearen Layout Kind
hier sind die Codes der mainactivity, detailactivity und die Fragmente: (I entfernt Einfuhren)
MainActivity
public class MainActivity extends AppCompatActivity implements ForecastFragment.Callback{
private final String LOG_TAG = MainActivity.class.getSimpleName();
private final String FORECASTFRAGMENT_TAG = "FFTAG";
private static final String DETAILFRAGMENT_TAG = "DFTAG";
private String mLocation;
boolean mTwoPane;
@Override
protected void onCreate(Bundle savedInstanceState) {
mLocation = Utility.getPreferredLocation(this);
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (findViewById(R.id.weather_detail_container) != null) {
// The detail container view will be present only in the large-screen layouts
// (res/layout-sw600dp). If this view is present, then the activity should be
// in two-pane mode.
mTwoPane = true;
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.weather_detail_container, new DetailFragment(), DETAILFRAGMENT_TAG)
.commit();
}
} else {
mTwoPane = false;
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
}
@Override
protected void onResume() {
super.onResume();
String location = Utility.getPreferredLocation(this);
// update the location in our second pane using the fragment manager
if (location != null && !location.equals(mLocation)) {
ForecastFragment ff = (ForecastFragment)getSupportFragmentManager().findFragmentById(R.id.fragment_forecast);
if (null != ff) {
ff.onLocationChanged();
}
DetailFragment df = (DetailFragment)getSupportFragmentManager().findFragmentByTag(DETAILFRAGMENT_TAG);
if (null != df) {
df.onLocationChanged(location);
}
mLocation = location;
}
}
@Override
public void onItemSelected(Uri contentUri) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
Bundle args = new Bundle();
args.putParcelable(DetailFragment.DETAIL_URI, contentUri);
DetailFragment fragment = new DetailFragment();
fragment.setArguments(args);
getSupportFragmentManager().beginTransaction()
.replace(R.id.weather_detail_container, fragment, DETAILFRAGMENT_TAG)
.commit();
} else {
Intent intent = new Intent(this, DetailActivity.class)
.setData(contentUri);
startActivity(intent);
}
}
}
ForecastFragment
public class ForecastFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final int FORECAST_LOADER = 0;
// For the forecast view we're showing only a small subset of the stored data.
// Specify the columns we need.
private static final String[] FORECAST_COLUMNS = {
// In this case the id needs to be fully qualified with a table name, since
// the content provider joins the location & weather tables in the background
// (both have an _id column)
// On the one hand, that's annoying. On the other, you can search the weather table
// using the location set by the user, which is only in the Location table.
// So the convenience is worth it.
WeatherContract.WeatherEntry.TABLE_NAME + "." + WeatherContract.WeatherEntry._ID,
WeatherContract.WeatherEntry.COLUMN_DATE,
WeatherContract.WeatherEntry.COLUMN_SHORT_DESC,
WeatherContract.WeatherEntry.COLUMN_MAX_TEMP,
WeatherContract.WeatherEntry.COLUMN_MIN_TEMP,
WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING,
WeatherContract.WeatherEntry.COLUMN_WEATHER_ID,
WeatherContract.LocationEntry.COLUMN_COORD_LAT,
WeatherContract.LocationEntry.COLUMN_COORD_LONG
};
// These indices are tied to FORECAST_COLUMNS. If FORECAST_COLUMNS changes, these
// must change.
static final int COL_WEATHER_ID = 0;
static final int COL_WEATHER_DATE = 1;
static final int COL_WEATHER_DESC = 2;
static final int COL_WEATHER_MAX_TEMP = 3;
static final int COL_WEATHER_MIN_TEMP = 4;
static final int COL_LOCATION_SETTING = 5;
static final int COL_WEATHER_CONDITION_ID = 6;
static final int COL_COORD_LAT = 7;
static final int COL_COORD_LONG = 8;
private ForecastAdapter mForecastAdapter;
public interface Callback {
/**
* DetailFragmentCallback for when an item has been selected.
*/
public void onItemSelected(Uri dateUri);
}
public ForecastFragment() {
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Add this line in order for this fragment to handle menu events.
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.forecastfragment, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_refresh) {
updateWeather();
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// The CursorAdapter will take data from our cursor and populate the ListView.
mForecastAdapter = new ForecastAdapter(getActivity(), null, 0);
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
// Get a reference to the ListView, and attach this adapter to it.
ListView listView = (ListView) rootView.findViewById(R.id.listview_forecast);
listView.setAdapter(mForecastAdapter);
// We'll call our MainActivity
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int position, long l) {
// CursorAdapter returns a cursor at the correct position for getItem(), or null
// if it cannot seek to that position.
Cursor cursor = (Cursor) adapterView.getItemAtPosition(position);
if (cursor != null) {
String locationSetting = Utility.getPreferredLocation(getActivity());
((Callback) getActivity())
.onItemSelected(WeatherContract.WeatherEntry.buildWeatherLocationWithDate(
locationSetting, cursor.getLong(COL_WEATHER_DATE)
));
}
}
});
return rootView;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
getLoaderManager().initLoader(FORECAST_LOADER, null, this);
super.onActivityCreated(savedInstanceState);
}
// since we read the location when we create the loader, all we need to do is restart things
void onLocationChanged() {
updateWeather();
getLoaderManager().restartLoader(FORECAST_LOADER, null, this);
}
private void updateWeather() {
FetchWeatherTask weatherTask = new FetchWeatherTask(getActivity());
String location = Utility.getPreferredLocation(getActivity());
weatherTask.execute(location);
}
@Override
public Loader<Cursor> onCreateLoader(int i, Bundle bundle) {
String locationSetting = Utility.getPreferredLocation(getActivity());
// Sort order: Ascending, by date.
String sortOrder = WeatherContract.WeatherEntry.COLUMN_DATE + " ASC";
Uri weatherForLocationUri = WeatherContract.WeatherEntry.buildWeatherLocationWithStartDate(
locationSetting, System.currentTimeMillis());
return new CursorLoader(getActivity(),
weatherForLocationUri,
FORECAST_COLUMNS,
null,
null,
sortOrder);
}
@Override
public void onLoadFinished(Loader<Cursor> cursorLoader, Cursor cursor) {
mForecastAdapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> cursorLoader) {
mForecastAdapter.swapCursor(null);
}
}
activity_main.xml
<?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="com.example.bassem.sunshine.app.MainActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_main" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
content_main.xml
<fragment 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/fragment"
android:name="com.example.bassem.sunshine.app.ForecastFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:layout="@layout/fragment_main" />
fragment_main.xml
<FrameLayout 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:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".ForecastFragment"
tools:showIn="@layout/activity_main">
<ListView
style="@style/ForecastListStyle"
android:id="@+id/listview_forecast"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
DetailActivity
public class DetailActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
if (savedInstanceState == null) {
Bundle arguments = new Bundle();
arguments.putParcelable(DetailFragment.DETAIL_URI, getIntent().getData());
DetailFragment fragment = new DetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.weather_detail_container, fragment)
.commit();
}
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.detail, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
startActivity(new Intent(this, SettingsActivity.class));
return true;
}
return super.onOptionsItemSelected(item);
}
}
DetailFragment
public class DetailFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> {
private static final String LOG_TAG = DetailFragment.class.getSimpleName();
static final String DETAIL_URI = "URI";
private static final String FORECAST_SHARE_HASHTAG = " #SunshineApp";
private ShareActionProvider mShareActionProvider;
private String mForecast;
private Uri mUri;
private static final int DETAIL_LOADER = 0;
private static final String[] DETAIL_COLUMNS = {
WeatherEntry.TABLE_NAME + "." + WeatherEntry._ID,
WeatherEntry.COLUMN_DATE,
WeatherEntry.COLUMN_SHORT_DESC,
WeatherEntry.COLUMN_MAX_TEMP,
WeatherEntry.COLUMN_MIN_TEMP,
WeatherEntry.COLUMN_HUMIDITY,
WeatherEntry.COLUMN_PRESSURE,
WeatherEntry.COLUMN_WIND_SPEED,
WeatherEntry.COLUMN_DEGREES,
WeatherEntry.COLUMN_WEATHER_ID,
// This works because the WeatherProvider returns location data joined with
// weather data, even though they're stored in two different tables.
WeatherContract.LocationEntry.COLUMN_LOCATION_SETTING
};
// These indices are tied to DETAIL_COLUMNS. If DETAIL_COLUMNS changes, these
// must change.
public static final int COL_WEATHER_ID = 0;
public static final int COL_WEATHER_DATE = 1;
public static final int COL_WEATHER_DESC = 2;
public static final int COL_WEATHER_MAX_TEMP = 3;
public static final int COL_WEATHER_MIN_TEMP = 4;
public static final int COL_WEATHER_HUMIDITY = 5;
public static final int COL_WEATHER_PRESSURE = 6;
public static final int COL_WEATHER_WIND_SPEED = 7;
public static final int COL_WEATHER_DEGREES = 8;
public static final int COL_WEATHER_CONDITION_ID = 9;
private ImageView mIconView;
private TextView mFriendlyDateView;
private TextView mDateView;
private TextView mDescriptionView;
private TextView mHighTempView;
private TextView mLowTempView;
private TextView mHumidityView;
private TextView mWindView;
private TextView mPressureView;
public DetailFragment() {
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Bundle arguments = getArguments();
if (arguments != null) {
mUri = arguments.getParcelable(DetailFragment.DETAIL_URI);
}
View rootView = inflater.inflate(R.layout.fragment_detail, container, false);
mIconView = (ImageView) rootView.findViewById(R.id.detail_icon);
mDateView = (TextView) rootView.findViewById(R.id.detail_date_textview);
mFriendlyDateView = (TextView) rootView.findViewById(R.id.detail_day_textview);
mDescriptionView = (TextView) rootView.findViewById(R.id.detail_forecast_textview);
mHighTempView = (TextView) rootView.findViewById(R.id.detail_high_textview);
mLowTempView = (TextView) rootView.findViewById(R.id.detail_low_textview);
mHumidityView = (TextView) rootView.findViewById(R.id.detail_humidity_textview);
mWindView = (TextView) rootView.findViewById(R.id.detail_wind_textview);
mPressureView = (TextView) rootView.findViewById(R.id.detail_pressure_textview);
return rootView;
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
// Inflate the menu; this adds items to the action bar if it is present.
inflater.inflate(R.menu.detailfragment, menu);
// Retrieve the share menu item
MenuItem menuItem = menu.findItem(R.id.action_share);
// Get the provider and hold onto it to set/change the share intent.
mShareActionProvider = (ShareActionProvider) MenuItemCompat.getActionProvider(menuItem);
// If onLoadFinished happens before this, we can go ahead and set the share intent now.
if (mForecast != null) {
mShareActionProvider.setShareIntent(createShareForecastIntent());
}
}
private Intent createShareForecastIntent() {
Intent shareIntent = new Intent(Intent.ACTION_SEND);
shareIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
shareIntent.setType("text/plain");
shareIntent.putExtra(Intent.EXTRA_TEXT, mForecast + FORECAST_SHARE_HASHTAG);
return shareIntent;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
getLoaderManager().initLoader(DETAIL_LOADER, null, this);
super.onActivityCreated(savedInstanceState);
}
void onLocationChanged(String newLocation) {
// replace the uri, since the location has changed
Uri uri = mUri;
if (null != uri) {
long date = WeatherContract.WeatherEntry.getDateFromUri(uri);
Uri updatedUri = WeatherContract.WeatherEntry.buildWeatherLocationWithDate(newLocation, date);
mUri = updatedUri;
getLoaderManager().restartLoader(DETAIL_LOADER, null, this);
}
}
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
if (null != mUri) {
// Now create and return a CursorLoader that will take care of
// creating a Cursor for the data being displayed.
return new CursorLoader(
getActivity(),
mUri,
DETAIL_COLUMNS,
null,
null,
null
);
}
return null;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
if (data != null && data.moveToFirst()) {
// Read weather condition ID from cursor
int weatherId = data.getInt(COL_WEATHER_CONDITION_ID);
// Use placeholder Image
mIconView.setImageResource(Utility.getArtResourceForWeatherCondition(weatherId));
// Read date from cursor and update views for day of week and date
long date = data.getLong(COL_WEATHER_DATE);
String friendlyDateText = Utility.getDayName(getActivity(), date);
String dateText = Utility.getFormattedMonthDay(getActivity(), date);
mFriendlyDateView.setText(friendlyDateText);
mDateView.setText(dateText);
// Read description from cursor and update view
String description = data.getString(COL_WEATHER_DESC);
mDescriptionView.setText(description);
// Read high temperature from cursor and update view
boolean isMetric = Utility.isMetric(getActivity());
double high = data.getDouble(COL_WEATHER_MAX_TEMP);
String highString = Utility.formatTemperature(getActivity(), high, isMetric);
mHighTempView.setText(highString);
// Read low temperature from cursor and update view
double low = data.getDouble(COL_WEATHER_MIN_TEMP);
String lowString = Utility.formatTemperature(getActivity(), low, isMetric);
mLowTempView.setText(lowString);
// Read humidity from cursor and update view
float humidity = data.getFloat(COL_WEATHER_HUMIDITY);
mHumidityView.setText(getActivity().getString(R.string.format_humidity, humidity));
// Read wind speed and direction from cursor and update view
float windSpeedStr = data.getFloat(COL_WEATHER_WIND_SPEED);
float windDirStr = data.getFloat(COL_WEATHER_DEGREES);
mWindView.setText(Utility.getFormattedWind(getActivity(), windSpeedStr, windDirStr));
// Read pressure from cursor and update view
float pressure = data.getFloat(COL_WEATHER_PRESSURE);
mPressureView.setText(getActivity().getString(R.string.format_pressure, pressure));
mForecast = String.format("%s - %s - %s/%s", dateText, description, high, low);
if (mShareActionProvider != null) { mShareActionProvider.setShareIntent(createShareForecastIntent());
}
}
}
@Override
public void onLoaderReset(Loader<Cursor> loader) { }
}
activity_detail.xml
<?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="com.example.bassem.sunshine.app.DetailActivity">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="@layout/content_detail" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/fab_margin"
android:src="@android:drawable/ic_dialog_email" />
</android.support.design.widget.CoordinatorLayout>
content_detail.xml
<fragment 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/weather_detail_container"
android:name="com.example.bassem.sunshine.app.DetailFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
tools:layout="@layout/fragment_detail" />
fragment_detail.xml
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<LinearLayout android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="16dp">
<TextView
android:id="@+id/detail_day_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/detail_date_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="16dp"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/detail_high_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/detail_low_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<ImageView
android:id="@+id/detail_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/detail_forecast_textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>
<TextView
android:id="@+id/detail_humidity_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/detail_wind_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/detail_pressure_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
Bitte lesen Sie [Erstellen eines ** minimalen **, vollständigen und überprüfbaren Beispiels] (http://stackoverflow.com/help/mcve). – Will