Androidでの複合コンポーネントの作成

すべてのHabr居住者およびAndroid愛好家へのご挨拶!

私たちの場合の複合とは、「複数で構成される」ことを意味しますが、それはすでに知っています。

だから、 タスクがあります:







実際には、それは何でも構いません-テキストフィールドのペアで構成される単純なテーブル行



またはチャート付きの複雑な財務ブロック





まず、私たちのケースに適用される代替オプションとその欠点を検討します。



カスタムコンポーネント


コンポーネントの設計と動作を変更できますが、同じコンポーネント内でのみ変更できますが、定義上、これは私たちには適していません。

例:

public class CustomImage extends ImageView { //... public CustomImage(Context context) { super(context); calcSize(); } void calcSize() { //  } //... }
      
      







ソフトウェアによる動的UI作成


このメソッドを使用するとキロバイトのコードを記述して各TextViewを手動で作成し、コンテキストを転送して、配置を記述するためのLayoutParamsを作成し、これをすべて以前に作成したLinearLayout / FrameLayout / RelativeLayoutに入れて、何百回もコードを実行する必要があります設計の一貫性を実現します。

そして、デザイナーがデザインの新しいバージョンを送信するとすぐに、穏やかに言えば、あなたはこれについてあまり満足しません...

コードで複数のフィールドを作成する抽象的な例:

 public void generateLayout() { LinearLayout linearLayout = new LinearLayout(getContext()); linearLayout.setOrientation(LinearLayout.VERTICAL); TextView name = new TextView(getContext()); name.setText(getContext().getResources().getText(R.string.channel_name).toString()); name.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 18);//18dip name.setTypeface(null, Typeface.BOLD); name.setPadding(20, 0, 20, 0); ViewGroup.MarginLayoutParams layoutParams = new ViewGroup.MarginLayoutParams( ViewGroup.MarginLayoutParams.FILL_PARENT, ViewGroup.MarginLayoutParams.WRAP_CONTENT); name.setLayoutParams(layoutParams); linearLayout.addView(name); for (int i = 0; i < 5; i++) { TextView subName = new TextView(getContext()); subName.setText(getChannelItemName(i)); subName.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16); subName.setTypeface(null, Typeface.NORMAL); subName.setPadding(30, 0, 20, 0); ViewGroup.MarginLayoutParams subLayoutParams = new ViewGroup.MarginLayoutParams( ViewGroup.MarginLayoutParams.FILL_PARENT, ViewGroup.MarginLayoutParams.WRAP_CONTENT); subName.setLayoutParams(subLayoutParams); linearLayout.addView(subName); } }
      
      





表レイアウト


実際、これは前の段落と同じで、親テーブルのセルのみが配置に使用されます。

この方法では、すべての配置が水平および垂直のみであるため、デザインを微妙にたどることができなくなります。コンポーネントを非標準の位置に移動するには、セルとテーブルセルに埋め込まれたレイアウトの結合に頼る必要があります。



キャンバスドロー


このメソッドの本質は、UIコンポーネントであるキャンバス上に単純に描画することです。

この方法には、ポイント2(設計に応じてすべてのUI要素を手動で調整するのが難しい)の欠点があるだけでなく、別の重要な欠点もあります-標準のEditText、Botton、CheckBox、SeekBarコントロールを使用できないこと手動で記述するか、UIの上にオーバーレイする必要があります。 いずれにせよ、問題を解決するための時間と労力の不十分な支出になります。

 public class DrawComponent extends View { public DrawComponent(Context context) { super(context); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //, , ... } }
      
      







LayoutInflaterを使用して複合コンポーネントを作成する


最後に、記事の本質に到達しました-与えられたタスクに応じて最適な方法でコンポーネントを作成します。

まず、すでに慣れているように、手動で、またはEclipse ADTプラグインの一部であるビジュアルエディターを使用して、XMLでレイアウト設計をレイアウトします。

UIのすべての主要な要素に固有のIDを必ず指定してください。

レイアウトの場合、RelativeLayoutを使用して、親の内部および相互に相対的なコンポーネントの相対位置を設定できるようにします。 具体的には、この場合、垂直のLinearLayoutで十分ですが、教育目的では簡単な方法を探していません。

コンポーネントの幅はハードに設定されます(288dip)。これは元の画像のようになりますが、「fill_parent」の実行を妨げるものはありません。



channel_layout.xml:

 <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/program_frame" android:layout_width="288dip" android:layout_height="wrap_content" android:padding="5dip"> <ImageView android:id="@+id/channel_logo" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:src="@drawable/russia"/> <TextView android:id="@+id/program_time" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/channel_logo" android:layout_alignLeft="@+id/channel_logo" android:layout_marginTop="5dip" android:singleLine="true" android:textColor="@android:color/black" android:textStyle="normal" android:textSize="12dp" android:text="25.07.2011 15:23"/> <TextView android:id="@+id/channel_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentTop="true" android:layout_alignParentRight="true" android:textColor="@android:color/black" android:textStyle="bold" android:textSize="16dp" android:singleLine="true" android:text=""/> <TextView android:id="@+id/program_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/program_time" android:layout_centerHorizontal="true" android:layout_marginTop="5dip" android:textColor="@android:color/black" android:textStyle="bold" android:textSize="15dp" android:singleLine="true" android:text="  "/> <TextView android:id="@+id/program_description" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/program_name" android:layout_centerHorizontal="true" android:layout_marginTop="5dip" android:textColor="@android:color/black" android:textStyle="normal" android:textSize="12dp" android:lines="3" android:text=", , !"/> <Button android:id="@+id/want_to_watch_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/program_description" android:layout_centerHorizontal="true" android:layout_marginTop="5dip" android:paddingLeft="10dip" android:paddingRight="10dip" android:textColor="@android:color/black" android:textStyle="bold" android:textSize="15dp" android:text=" "/> </RelativeLayout>
      
      





テキストのプロパティを設定するには、いくつかのスタイルを作成できますが、わかりやすくするために、私たちが持っているものでうまくいきましょう。 また、strings.xmlにテキストラベルを作成しなかったためにキックしないでください。読みやすさが低下し、記事内の別のファイルを引用する必要があります。

次に、コンポーネントのクラスを作成し、レイアウトで使用したクラス(RelativeLayout)から継承します。

クラスとchannel_layoutレイアウトを接続するために、LayoutInflaterを使用します。

また、クラス内のすべてのフィールドに変数を定義して、クラスのフィールドをUIに接続します。

 public class ChannelFrame extends RelativeLayout { private TVProgram parentProgram; private ImageView channel_logo; private TextView channel_name; private TextView program_time; private TextView program_name; private TextView program_description; private Button want_to_watch_button; private String programName = ""; private boolean isWannaWatch = false; public ChannelFrame(Context context) { super(context); initComponent(); } private void initComponent() { LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.channel_layout, this); channel_logo = (ImageView) findViewById(R.id.channel_logo); channel_name = (TextView) findViewById(R.id.channel_name); program_time = (TextView) findViewById(R.id.program_time); program_name = (TextView) findViewById(R.id.program_name); program_description = (TextView) findViewById(R.id.program_description); want_to_watch_button = (Button) findViewById(R.id.want_to_watch_button); want_to_watch_button.setOnClickListener(buttonListener); updateFields(); } private void updateFields() { if (isWannaWatch) { program_name.setText(programName + "*"); this.setBackgroundResource(R.drawable.frame_bg_selected); } else { program_name.setText(programName); this.setBackgroundResource(R.drawable.frame_bg); } } public void setChannelName(String name) { channel_name.setText(name); } public void setChannelLogo(int resourceId) { channel_logo.setImageResource(resourceId); } public void setChannelLogo(Bitmap image) { channel_logo.setImageBitmap(image); } public void setProgramTime(String time) { program_time.setText(time); } public void setProgramName(String name) { programName = name; program_name.setText(programName); } public void setProgramDescription(String name) { program_description.setText(name); } private final OnClickListener buttonListener = new OnClickListener() { public void onClick(View view) { isWannaWatch = !isWannaWatch; updateFields(); } }; public TVProgram getParentProgram() { return parentProgram; } public void setParentProgram(TVProgram parentProgram) { this.parentProgram = parentProgram; updateFieldsByParent(); } private void updateFieldsByParent() { setProgramName(parentProgram.getName()); setProgramDescription(parentProgram.getDesc()); setProgramTime(SimpleDateFormat.getInstance().format(parentProgram.getTime())); setChannelLogo(parentProgram.getChannelLogo()); setChannelName(parentProgram.getChannelName()); } }
      
      





さて、ここで私がやったこと:まず、すべてのフィールドを初期化し、フィールド値を設定するための便利なメソッドを作成します。たとえば、ロゴの設定には、リソースのIDを示す方法とビットマップを送信する方法の2つの方法があります。

クラスは「TVProgram parentProgram」のラッパーでもあります-これはUIコンポーネントのフィールドを設定する別の方法です-setParentProgramを呼び出して、入力されたプログラムオブジェクトを渡すことにより、親からすべてのUIフィールドの値を自動的に設定します。

コンポーネントは準備ができており、インスタンスを作成し、フィールド値を設定し、フォームに追加します。

 public class StartActivity extends Activity { private LinearLayout framesContainer; /** * Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); framesContainer = (LinearLayout) findViewById(R.id.frames_container); for (int i = 0; i < 5; i++) { ChannelFrame frame = new ChannelFrame(getApplicationContext()); frame.setProgramName("............."); frame.setProgramDescription("............."); frame.setProgramTime("............."); framesContainer.addView(frame); } } }
      
      





そして最後に、得られたもののスクリーンショット:



そしてソース

あなたに成功したプロジェクト!



All Articles