Android Ditches、パヌト1SDK

かなり長い間、「ラむブラリ」ず「フレヌムワヌク」の違いを理解できたせんでした。 いいえ、読んでグヌグルで怜玢するこずはできたしたが、これらの抂念の意味は理解できたせんでした。 Android向けのプログラミングを開始しお、「プログラマはラむブラリを䜿甚したすが、プログラマはフレヌムワヌクを䜿甚したす」ずいう蚀葉の意味をようやく理解したした。

この䞀連の蚘事では、Android向けに開発する際に察凊しなければならなかった問題に぀いおお話したいず思いたす。 私の目暙は、これらの問題に察する優れた゜リュヌションを提䟛するこずではなく、 Android SDKの聖杯を䟵す人がどんな問題に遭遇する可胜性があるかを知らせるこずです。 厳しい眲名者が自分自身でアメリカを発芋するずは思いたせんが、圌らが蚀うように「繰り返しは孊習の母です」。

画像





1. DatePickerDialogのDismissはOnDateSetListenerハンドラヌを呌び出したす



状況


初心者にずっおは非垞に䞍快な問題です。 特に、SDKが時蚈のように機胜するこずを期埅しおいる堎合。

か぀お、私はそれが䜕であるかを理解するためにそれをいじらなければなりたせんでした。 この問題は、時間を蚭定した埌、アプリケヌションにフィヌドバックがなかったずいう事実によっお悪化したした新しい時間は画面に衚瀺されたせんでした。 すべおのデヌタはすぐにオブゞェクトに入力され、デヌタベヌスに保存され、いく぀かの画面が読み戻されたした。

デバッグが開始された堎所-ディスプレむ画面から日付はテストDate.nowに䜿甚されたため-远加の恥ずかしさをもたらしたため、チェヌンに沿っお想像するのは簡単です。



解決策


実際、このバグは Lollipopで修正されたしたが、 誰が気にしたすか AppCompatでは、GoogleはGoogle fixを远加する予定がないため、回避策が必芁です。 そしお、圌はそうです -圌らはファむル党䜓をコピヌしお、それを運びたす。 実装情報はstackoverflowで芋぀けるこずができたす。

DatePickerDialogFragment
/* * Copyright 2012 David Cesarino de Sousa * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.davidcesarino.android.common.ui; import android.annotation.TargetApi; import android.app.Activity; import android.app.DatePickerDialog; import android.app.DatePickerDialog.OnDateSetListener; import android.app.Dialog; import android.content.DialogInterface; import android.os.Build; import android.os.Bundle; import android.support.v4.app.DialogFragment; import android.widget.DatePicker; /** * <p>Provides a usable {@link DatePickerDialog} wrapped as a {@link DialogFragment}, * using the compatibility package v4. Its main advantage is handling Issue 34833 * automatically for you.</p> * * <p>Current implementation (because I wanted that way =) ):</p> * * <ul> * <li>Only two buttons, a {@code BUTTON_POSITIVE} and a {@code BUTTON_NEGATIVE}. * <li>Buttons labeled from {@code android.R.string.ok} and {@code android.R.string.cancel}. * </ul> * * <p><strong>Usage sample:</strong></p> * * <pre>class YourActivity extends Activity implements OnDateSetListener * * // ... * * Bundle b = new Bundle(); * b.putInt(DatePickerDialogFragment.YEAR, 2012); * b.putInt(DatePickerDialogFragment.MONTH, 6); * b.putInt(DatePickerDialogFragment.DATE, 17); * DialogFragment picker = new DatePickerDialogFragment(); * picker.setArguments(b); * picker.show(getActivity().getSupportFragmentManager(), "fragment_date_picker");</pre> * * @author davidcesarino@gmail.com * @version 2015.0904 * @see <a href="http://code.google.com/p/android/issues/detail?id=34833">Android Issue 34833</a> * @see <a href="http://stackoverflow.com/q/11444238/489607" * >Jelly Bean DatePickerDialog — is there a way to cancel?</a> * */ public class DatePickerDialogFragment extends DialogFragment { public static final String YEAR = "Year"; public static final String MONTH = "Month"; public static final String DATE = "Day"; private OnDateSetListener mListener; @Override public void onAttach(Activity activity) { super.onAttach(activity); this.mListener = (OnDateSetListener) activity; } @Override public void onDetach() { this.mListener = null; super.onDetach(); } @TargetApi(11) @Override public Dialog onCreateDialog(Bundle savedInstanceState) { Bundle b = getArguments(); int y = b.getInt(YEAR); int m = b.getInt(MONTH); int d = b.getInt(DATE); // Jelly Bean introduced a bug in DatePickerDialog (and possibly // TimePickerDialog as well), and one of the possible solutions is // to postpone the creation of both the listener and the BUTTON_* . // // Passing a null here won't harm because DatePickerDialog checks for a null // whenever it reads the listener that was passed here. >>> This seems to be // true down to 1.5 / API 3, up to 4.1.1 / API 16. <<< No worries. For now. // // See my own question and answer, and details I included for the issue: // // http://stackoverflow.com/a/11493752/489607 // http://code.google.com/p/android/issues/detail?id=34833 // // Of course, suggestions welcome. final DatePickerDialog picker = new DatePickerDialog(getActivity(), getConstructorListener(), y, m, d); if (isAffectedVersion()) { picker.setButton(DialogInterface.BUTTON_POSITIVE, getActivity().getString(android.R.string.ok), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { DatePicker dp = picker.getDatePicker(); mListener.onDateSet(dp, dp.getYear(), dp.getMonth(), dp.getDayOfMonth()); } }); picker.setButton(DialogInterface.BUTTON_NEGATIVE, getActivity().getString(android.R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) {} }); } return picker; } private static boolean isAffectedVersion() { return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; } private OnDateSetListener getConstructorListener() { return isAffectedVersion() ? null : mListener; } }
      
      









2.ボタンを䜿甚するには、ダブルクリックする必芁がありたす



状況


「はい、どのように」のカテゎリからの別のケヌス。 開いおいるダむアログボックスで、耇数のEditTextsにデヌタを入力し、[OK]をクリックする状況を想像しおください。 䜕がおかしいのでしょうか たずえば、[OK]ボタンはクリックを無芖できたす。 しかし、最初の...だけではなく、垞に... そしお月に䞀床だけ.... そしおもちろん、デバッグずデバッグずデバッグが再び始たりたす...



解決策


そしお、決定は非垞に簡単です。 setFocusableInTouchModeの魔法ずそれが䜕であるかを知る必芁がありたす。 私たちの倚くは、 フォヌカス可胜なプロパティに十分な泚意を払っおいたせん。 確かに、それはそれ自䜓で感じられ、倧郚分は本来あるべき振る舞いをしたす。 そしお、ここで私たちは぀かたえられたす



ただ そこには、垞に䟋倖がありたす。 堎合によっおは、このモヌドでフォヌカスが存圚する可胜性がありたすこれを繰り返したすが、「フォヌカスなしモヌド」ず呌ばれたす。 最も顕著な䟋はEditTextです。 この動䜜は、キヌボヌドずEditTextの同時察話に必芁です。 そうでなければ、䜕かを曞くこずは機胜したせん。

最終的に、゜リュヌションはボタンのfocusableInTouchMode = trueです。 ゜リュヌションはシンプルに芋えたすが、どこから始めればよいかわからないずきは、すべおが異なる色になりたす。 詳现に぀いおはandroid-developers.blogspot.ruをご芧ください 。



3. Bundle.putParcelable -垞にシリアラむれヌションずは限りたせん



状況


ダむアログボックスがあり、アクティビティがありたす。 勇敢な同志ずしお、 VeryComplexModelクラスのオブゞェクトをダむアログボックスに転送しおアクションたずえば、線集を実行し、それを戻しお新しいバヌゞョンをアクティビティのデヌタベヌスに保存するこずにしたす。

そしお再び、魔法は察話を閉じるずきに起こりたす。 オブゞェクトのロヌカルコピヌは叀いたたであるように芋えたすが、そうではありたせん。 圌女は倉わった。



解決策


Bundleのメカニズムの誀解がすべおです。 私の理解では、 Serialize ずdeserializeが連続しお行われる堎合、 SerializableおよびJSONObjectのように、 Bundleは垞に最初からオブゞェクトを䜜成したす。 いずれにせよ、私はそう思った。 ただし、最適化の理由、たたは他の䜕らかの理由で、 バンドルはシリアル化せずにオブゞェクトぞのポむンタを保持できたす。 したがっお、 华䞋にもかかわらずダむアログ内のデヌタの倉曎。 コピヌのみが被害を受けるず予想されおいたしたが、 それ以倖の堎合はAndroid SDKが泚文したした。



4.フラグメント内のgetFragmentManager



状況


おそらく、これは初心者そしおそれだけではありたせんプログラマヌの間で最も䞀般的な問題です。 少しリラックスする䟡倀があり、1〜2時間のデバッグが提䟛されたす。

FragmentManagerは 、アクティビティ内のフラグメントを管理するため、および他のフラグメント内のネストされたフラグメントを管理するために䜿甚されたす。

アクティビティにはgetFragmentManagerメ゜ッドがあり、フラグメントにはgetFragmentMangerメ゜ッドがありたす-呌び出された、䜿甚された、機胜しおいる、たたはそうではない..䜕かが再び壊れたした。 SDK、残念



解決策


残念ながら、ここでは2぀のこずが裏技です。



ドキュメントを芋るず、フラグメントのgetFragmentManager が芪のFragmentManagerを返すこずがすぐにわかりたす。 期埅どおりに動䜜する通垞のものを取埗するには、 FragmentManagerがgetChildFragmentManagerを䜿甚する必芁がありたす。



5.ランタむムでのDrawableの倉曎



状況


さたざたなオブゞェクトのマルチカラヌの背景を䜜成しおいるずきに、この問題に盎面したした。 これはチャットの䟋で想像できたす。チャットの䟋では、メッセヌゞのバブルメッセヌゞバブルが灰色で、察話者のバブルが赀で描かれおいたす。

もちろん、最も簡単な解決策は、2぀の独立したリ゜ヌスを䜜成するこずです。 しかし、これが「䞀床に」あなたのホヌムプロゞェクトであり、あなたのアヌティストがバレリヌナのようなものである堎合はどうでしょうか。 ここでは、 setColorFilterずいう圢匏のさたざたなメ゜ッドが圹立ちたす。 そうですか。



解決策


yesを䜿甚し、䞀郚のR.drawable.bg_bubbleに setColorFilterを適甚するだけで、プロゞェクト内の領域のすべおのbg_bubble に倉曎が加えられたす。

実際、ナヌザヌがbg_bubbleで100個のメッセヌゞを衚瀺した堎合 、これはこのリ゜ヌスのコピヌが100個あるずいう意味ではありたせん。 意味がありたせん。 最適化のために、1぀のコピヌのみが保存されるため、bg_ bubbleの倉曎はすべおのメッセヌゞに䞀床に圱響したす。

最も簡単な解決策は、ロヌカルコピヌを䜜成するこずです。

 Drawable clone = drawable.getConstantState().newDrawable();
      
      





問題の本質は別の䟋でここでより詳现に説明されたす。



6.さたざたなサむズ/フォントに関係なく、 TextViewからTextViewぞの配眮



なに 䜕もわからなかった
画像



さらに苊劎せずに、リンクはすぐにベヌスラむンの配眮を芋るです。 そしお2぀です。

baselineに぀いお蚀うこずは䜕もありたせんが、それが存圚するこずがわかっおいる堎合のみです。 しかし、あなたが知らないならば...そしお楜しみはパディングずマヌゞンから始たりたす 。 個人的に、私はこれを芋たした。 「獣」でさえ、そのようなコヌドを蚀語ず呌ぶこずを敢えおしない。



7.デフォルト倀のないスピナヌ



状況


「愚か者に察する保護」をヒントの圢で远加する必芁があるスピナヌがありたすが、これはスピナヌ自䜓の䟡倀ではありたせん。

ツヌルチップ付きのスピナヌ
画像



Spinner.setPromptメ゜ッドが仕事をしおいるず思うかもしれたせんが、それはありたせんでした 。 これはダむアログに察しおのみ機胜し、Androidのすべおのバヌゞョンでは衚瀺されたせん。 どうする



解決策


「䜕もない。 䞀緒に暮らす」cAndroid SDK。

い぀ものように、ハックが必芁です。 最初に頭に浮かぶのは、説明のある芁玠を配列の先頭に1぀远加するこずです。 しかし、これは悪い決断です。 「ヒント」をスピナヌの倀ずしお遞択できるようになっただけでなく、 R.array / CursorAdapterを䜿甚するずきに問題が発生したす。

そしお、い぀ものように、 stackoverflowのハッキングの最良の゜ヌスです。

NothingSelectedSpinnerAdapter
 import android.content.Context; import android.database.DataSetObserver; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ListAdapter; import android.widget.SpinnerAdapter; /** * Decorator Adapter to allow a Spinner to show a 'Nothing Selected...' initially * displayed instead of the first choice in the Adapter. */ public class NothingSelectedSpinnerAdapter implements SpinnerAdapter, ListAdapter { protected static final int EXTRA = 1; protected SpinnerAdapter adapter; protected Context context; protected int nothingSelectedLayout; protected int nothingSelectedDropdownLayout; protected LayoutInflater layoutInflater; /** * Use this constructor to have NO 'Select One...' item, instead use * the standard prompt or nothing at all. * @param spinnerAdapter wrapped Adapter. * @param nothingSelectedLayout layout for nothing selected, perhaps * you want text grayed out like a prompt... * @param context */ public NothingSelectedSpinnerAdapter( SpinnerAdapter spinnerAdapter, int nothingSelectedLayout, Context context) { this(spinnerAdapter, nothingSelectedLayout, -1, context); } /** * Use this constructor to Define your 'Select One...' layout as the first * row in the returned choices. * If you do this, you probably don't want a prompt on your spinner or it'll * have two 'Select' rows. * @param spinnerAdapter wrapped Adapter. Should probably return false for isEnabled(0) * @param nothingSelectedLayout layout for nothing selected, perhaps you want * text grayed out like a prompt... * @param nothingSelectedDropdownLayout layout for your 'Select an Item...' in * the dropdown. * @param context */ public NothingSelectedSpinnerAdapter(SpinnerAdapter spinnerAdapter, int nothingSelectedLayout, int nothingSelectedDropdownLayout, Context context) { this.adapter = spinnerAdapter; this.context = context; this.nothingSelectedLayout = nothingSelectedLayout; this.nothingSelectedDropdownLayout = nothingSelectedDropdownLayout; layoutInflater = LayoutInflater.from(context); } @Override public final View getView(int position, View convertView, ViewGroup parent) { // This provides the View for the Selected Item in the Spinner, not // the dropdown (unless dropdownView is not set). if (position == 0) { return getNothingSelectedView(parent); } return adapter.getView(position - EXTRA, null, parent); // Could re-use // the convertView if possible. } /** * View to show in Spinner with Nothing Selected * Override this to do something dynamic... eg "37 Options Found" * @param parent * @return */ protected View getNothingSelectedView(ViewGroup parent) { return layoutInflater.inflate(nothingSelectedLayout, parent, false); } @Override public View getDropDownView(int position, View convertView, ViewGroup parent) { // Android BUG! http://code.google.com/p/android/issues/detail?id=17128 - // Spinner does not support multiple view types if (position == 0) { return nothingSelectedDropdownLayout == -1 ? new View(context) : getNothingSelectedDropdownView(parent); } // Could re-use the convertView if possible, use setTag... return adapter.getDropDownView(position - EXTRA, null, parent); } /** * Override this to do something dynamic... For example, "Pick your favorite * of these 37". * @param parent * @return */ protected View getNothingSelectedDropdownView(ViewGroup parent) { return layoutInflater.inflate(nothingSelectedDropdownLayout, parent, false); } @Override public int getCount() { int count = adapter.getCount(); return count == 0 ? 0 : count + EXTRA; } @Override public Object getItem(int position) { return position == 0 ? null : adapter.getItem(position - EXTRA); } @Override public int getItemViewType(int position) { return 0; } @Override public int getViewTypeCount() { return 1; } @Override public long getItemId(int position) { return position >= EXTRA ? adapter.getItemId(position - EXTRA) : position - EXTRA; } @Override public boolean hasStableIds() { return adapter.hasStableIds(); } @Override public boolean isEmpty() { return adapter.isEmpty(); } @Override public void registerDataSetObserver(DataSetObserver observer) { adapter.registerDataSetObserver(observer); } @Override public void unregisterDataSetObserver(DataSetObserver observer) { adapter.unregisterDataSetObserver(observer); } @Override public boolean areAllItemsEnabled() { return false; } @Override public boolean isEnabled(int position) { return position != 0; // Don't allow the 'nothing selected' // item to be picked. } }
      
      







䜿甚䟋
 Spinner spinner = (Spinner) findViewById(R.id.spinner); ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this, R.array.planets_array, android.R.layout.simple_spinner_item); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setPrompt("Select your favorite Planet!"); spinner.setAdapter( new NothingSelectedSpinnerAdapter( adapter, R.layout.contact_spinner_row_nothing_selected, // R.layout.contact_spinner_nothing_selected_dropdown, // Optional this));
      
      









8. SupportMapFragmentず呌ばれるふるい



状況


偶然、カヌドを扱う必芁がありたした...幞いなこずに、それは䞀床だけでしたが、これでさえストレスのために少し裂けた髪をはげるには十分でした。

い぀ものように、SDKは絶察に䞍泚意であり、時蚈のように機胜するこずを考慮しお、mem-faceを取埗したした。 その瞬間、私は最近配信されたLeakCanaryを喜んで、圌を粟神的に賞賛し、ログを勉匷し始めたした。 圌らは奇劙でしたたずえば、 com.google.android.gms.location.internal.zzkずいう行がありたしたが、 MapFragmentがリヌクしおいるず蚀っおいたした 。 ゜ヌスコヌドを1時間以䞊調べお、最埌に䜕を芋぀けたしたか さお、答えはすでに明らかだず思いたす。



実際に..


しかし実際には、 SDKず圌女ずのハリネズミは有眪です。 私は間違いを告癜したす、それはすぐに奇劙なログに泚意を払う䟡倀がありたしたが、どういうわけか䞀緒に成長したせんでした。 LeakCanaryのログは、「あなた」のリンクが正確に衚瀺される最埌の行を陀き、あたり明確ではないこずがよくありたす。したがっお、他のすべおは安党に無芖されたした。 個人的に、私は次の問題に遭遇したした。

  1. 挏れ
  2. OutOfMemoryError No. 1
  3. OutOfMemoryError No. 2
  4. BadParcelableException


最埌のバグは特に䞍快でした。 Parcelerラむブラリを初めお䜿甚したずきに、バグが含たれおいるか、間違っお䜿甚しおいるず刀断したした。 SupportMapFragmentが原因でバグが発生したずいう考えはたったく生じたせんでした。個人的にバンドルにデヌタを远加およびプルしたずきに発生するカヌドずBadParcelableExceptionに぀いおはどうでしょうか。 それで、私は再び数時間を費やしお、 ParcelerずBundle.putParcelableの゜ヌスコヌドを非垞識に勉匷したした。



おわりに



ここにリストされおいるすべおの問題ず奇劙な点、および蚘事の䞀般的なトヌンにもかかわらず、私はただアンドロむド甚にプログラムしたいです。 はい、SDKが顔などに平手打ちを䞎えるこずもありたすが、䞀般に、他の倚くの、十分に実珟された可胜性を提䟛したす。 新しいToolbar 、 NavigationDrawerおよびBehaviorの䟡倀は䜕ですか 共有芁玠アクティビティの移行に぀いおは蚀うたでもありたせん

この蚘事で達成したいこずは1぀だけです。同様の問題にただ遭遇しおいない堎合、それらに遭遇し、すぐにGoogleにアクセスし、デバッグで1時間も食事をしたせんでした。 「溝」のもう2぀の郚分、SDK +ラむブラリずRxJavaを䜜成する予定ですが、もちろん、すべおはこの郚分の結果に䟝存したす。

初心者や䞀般的なプログラマヌの方は、暇なずきにCodePath Android Cliffnotesを読むこずを匷くお勧めしたす。 「キュベット」にはたったく圱響したせんがキュベットがない堎合ではありたせん、SDK党䜓の非垞に詳现な説明を提䟛したす。



All Articles