androidのSpinnerのDropDownListの背景を透過にする&文字色変更の修正版

久しぶりに前に書いたプログラムをいじっていたら、このルーチンが動作しないことに気がつきました。
前回作成時から4年経ち、開発環境もEclipseからAndroidStudioに変えて再作成してみました。 といっても変更箇所はあまり多くありませんでした。
Android Studio 1.5.1
Minimum SDK Android 2.2
Compile Sdk Version API 23
Build Tool Version 23.0.3

1 android 4.0 (APIレベル14) 以上のエミュで実行しようとすると落ちる。

自作アダプタで背景を透明にする部分で落ちるようでした。
((View) parent.getParent()).setBackgroundDrawable(getResources().getDrawable(R.drawable.transparent_button01));
の部分です。
元々危ないコードとは思っていたのですが、やはり危なかったみたいです。
ここをコメントアウトすると落ちなくなりました。
以下Try and Error の結果です。なお android3.x (API 11-13) については試していません。

@落ちる原因を探すためにエラーメッセージを見ていたら、parent.getParent()がNULLになる時があることに気づきました。
NULLで無い場合でもいくつか別のViewになっているみたいでした。
試した結果、
APIレベル14以上ではparent.getParent()がPopupWindowの時に設定すれば良いみたいでした。
APIレベル10以下では
メインのclassにAppCompatActivityを使った場合parent.getParent()がPopupWindowの時に設定すれば良いみたいでした。
メインのclassにActivityを使った場合parent.getParent()がLinearLayoutの時に設定すれば良いみたいでした。

AAPI 16 (android4.1) 以上ではspinnerのドロップダウンリストの背景が設定できるようになっています
spinner作成時に
spinner.setPopupBackgroundResource(R.drawable.list_border);
のようにすれば、作成した背景XMLが適用できます。
API 16 未満でこのコードを実行すると落ちます。

XMLを動的に変更、作成して適用することもできます。
背景色と線色を変えたい場合

import android.graphics.drawable;
GradientDrawable sp_drawable=(GradientDrawable)getResources().getDrawable(R.drawable.list_border);
sp_drawable.setColor(0x880000ff);
sp_drawable.setStroke(2,0xff0000ff);
spinner.setPopupBackgroundDrawable(sp_drawable);

とすることで、線が青、背景は青の透過にできます。
さらにXMLファイルを用意しないで

GradientDrawable sp_drawable = new GradientDrawable();
sp_drawable.setColor(0x880000ff);
sp_drawable.setStroke(2, 0xff0000ff);
sp_drawable.setCornerRadius(5);
spinner.setPopupBackgroundDrawable(sp_drawable);

としても同じ事ができます。
なおこの場合の線幅などはピクセル単位のようなので、上のままだと高解像度画面では線が細く、角が鋭になります
デザインを重視するならdipからの変換が必要と思われます。
例えばsetStroke(2, 0xff0000ff);の「2」の部分は

float density = getResources().getDisplayMetrics().density;
int px = (int) (2f * density + 0.5f);
sp_drawable.setStroke(px, 0xff0000ff);

のようにして「2」の部分に「px」を入れます。

BAPI 14 (android4.0) 以上ではXMLファイルでドロップダウンリストの半透明化が可能です
spinner作成時のXMLファイルで
android:popupBackground="#ccffffff"
と設定することで半透明化が可能です。
android:popupBackground="@drawable/list_border"
のように指定して背景XMLを設定することもできます。

APIレベル10以下でもメインのclassにAppCompatActivityを使った場合同様の方法で半透明化が可能です
背景XMLの指定も可能ですが完全に透明になってしまいました。少し挙動が異なるようです。
メインのclassにActivityを使った場合、設定は無視されるので@の方法でしかできません。





2 ドロップダウンリストの高さが狭い(低い?)。

API 14以上で実行するとドロップダウンリストの高さが文字1文字分になってしまい、押しにくくなってしまいました。
最初、自作XMLファイルを当てて対処しようと思ったのですが、ネットで検索していて以下のことに気づきました。
自作AdapterのgetDropDownViewとgetView内にある
この行
convertView = inflater.inflate(resourceId, null);
ではだめで
convertView = inflater.inflate(resourceId, parent, false);
にする必要があるようです。


3 ドロップダウンリストにチェックボックス(ラジオボタン)が表示されない。

これはAPI 14(android4.0)以降の変更のようです。
API 10以下ではボックスは表示されますが、チェックは入りません。
こういう仕様のようなので仕方ないと思いましょう。
ちなみにAPI 10以下用のプログラムが必要で従来通りチェックボックスにチェックが入るようにしたい場合
メインのclassにAppCompatActivityではなくActivityを使えば良いみたいです。

以上いろいろ試した結果で、動作を保証するものではありません。





左はAPI 8 (android 2.2) 右はAPI 23 (android 6.0)で表示させたところです。




コードは以下の通りです。
ドロップダウンリストの文字色を変える方法は今回も判らなかったので、前回同様自作Adapterで対処しています。
背景を半透過にする処理は
API 14 (android4.0) 以上ではXMLファイルで指定。
API 10 以下では Viewの種類を確認してから、前回の方法で対処しました。
主な背景に関する部分を赤で表示しています。


activity_main.xml layoutに置きます
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/backimage"
    tools:context="namespace.test.spinner.spinnertestactivity.MainActivity" >

    <Spinner
        android:id="@+id/spinner1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="48dp"/>

    <Spinner
        android:id="@+id/spinner2"
        android:background="@drawable/list_border"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="48dp"
        android:popupBackground="@drawable/list_border" />

</LinearLayout>




list_border.xml drawableに置きます
<?xml version="1.0" encoding="utf-8" ?>
<shape  xmlns:android="http://schemas.android.com/apk/res/android"
android:shape ="rectangle">
<corners android:radius="5dip" />
<solid android:color="#AAFFFFFF"/> 
<padding
android:left="10dip"
android:right="10dip" />
<stroke
android:width="2dip"
android:color="#CCCCCC" />
</shape>




backimage.xml drawableに置きます
drawableにic_launcher.pngを1個コピーしてください
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
    android:tileMode="repeat"
    android:src="@drawable/ic_launcher" />




MainActivity.java
package namespace.test.spinner.spinnertestactivity;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.TextView;

public class MainActivity extends AppCompatActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        String[] s_items = {"RED", "BLUE", "BLACK", "YELLOW", "GREEN"};
        //普通のSpinner
        Spinner spinner1 = (Spinner)findViewById(R.id.spinner1);
        ArrayAdapter<String> adapter1 =
    		new ArrayAdapter<String>(this, android.R.layout.simple_spinner_item, s_items);
        adapter1.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner1.setAdapter(adapter1);
        //カスタマイズSpinner
        Spinner spinner2 = (Spinner)findViewById(R.id.spinner2);
        IntentItemArrayAdapter adapter2 =
        	new IntentItemArrayAdapter(this, android.R.layout.simple_spinner_item, s_items);
        adapter2.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
        spinner2.setAdapter(adapter2);
    }

    private class IntentItemArrayAdapter extends ArrayAdapter<String> {
        private int resourceId;
        private int resourceId2;
        public IntentItemArrayAdapter(Context context, int resourceId, String[] items) {
            super(context, resourceId,items);
            this.resourceId = resourceId;
            this.resourceId2 = resourceId;
        }
        public void setDropDownViewResource(int resourceId2){
            this.resourceId2 = resourceId2;
        }
        @Override
        public View getDropDownView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                LayoutInflater inflater = (LayoutInflater) getContext()
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                        convertView = inflater.inflate(resourceId2,  parent, false);
            }
            TextView tv = (TextView) convertView.findViewById(android.R.id.text1);
            String cs=this.getItem(position);
            tv.setText(cs);
            tv.setTextColor(Color.parseColor(cs));//ここでリストの文字色を指定
            if(parent.getParent()!=null){
                        if(android.os.Build.VERSION.SDK_INT <=10 && parent.getParent().toString().indexOf("PopupWindow")>0){
                              ((View) parent.getParent()).setBackgroundDrawable(getResources().getDrawable(R.drawable.list_border));//リストの背景画像を指定
                        }
            }
            return convertView;
        }
        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            if (convertView == null) {
                LayoutInflater inflater = (LayoutInflater) getContext()
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                convertView = inflater.inflate(resourceId,  parent, false);
            }
            TextView tv = (TextView) convertView.findViewById(android.R.id.text1);
            String cs=this.getItem(position);
            tv.setText(cs);
            tv.setTextColor(Color.parseColor(cs));//ここでスピナの文字色を指定
              return convertView;
        }
    }


}






参考リンク
Spinnerのカスタマイズ
XMLの設定などが書いてありました。

How to Customize Spinner in Android
英文ですがSpinnerのリストに画像を表示しています。

AndroidでViewの枠線と背景色を動的に変更する
XMLを動的に変更する方法が書かれています。

Android dip, dp, から pt, px に変換する
dip, dp, から pt, px に変換する方法が書かれています。



TOPに戻る
2016/5/5