2013-02-18 3 views
11

Я хочу расширить LinearLayout, чтобы при рисовании моего макета под ним добавлялась тень. Я играл вокруг переопределения метода onDraw, но я немного потерялся. Любая помощь в этом или даже библиотечных предложениях была бы с благодарностью!Расширение Android Просмотр класса для добавления dropshadow

Вот пример представления тени, которого я пытаюсь достичь. Я не верю, что могу использовать девять патчей, потому что мне нужно, чтобы содержимое представления находилось в белом ящике. Это означало бы, что мне нужно знать расстояние между границей и концом PNG. Однако я считаю, что разная плотность экрана означает, что это расстояние всегда будет одним и тем же PX, но не тем же DP.

Чтобы быть ясным, мне нужен способ расширить класс вида, чтобы при его добавлении к макету рисовалась тень под ним. Нет решений XML или 9Patch.

enter image description here

Благодаря

Джек

+0

Я думаю, вы могли бы нарисовать черный прямоугольник, который имеет размер вашей точки зрения, а затем поверх него обратить ваше мнение, но перевел немного на вниз и вправо (или вниз и влево, независимо от того, что вы хотите). –

+0

Не могли бы вы рассказать, как можно это сделать? – JackMahoney

+0

'Вот пример. Я не могу использовать 9-патч либо Почему бы и нет? Это лучший и простой подход. – JanithaR

ответ

14

Я согласен с замечаниями по вашему вопросу: программный DropShadow эффект плохой выбор, и вы могли бы добиться того же эффекта с помощью простого 9patch (или набор из них), как заявил here.

BTW Мне было слишком любопытно, и я закончил с взломом решения после работы.

Представленный код является тестом и должен быть предназначен как простая доказательная концепция (так что, пожалуйста, не делайте снизу). Некоторые из приведенных операций: довольно дорогие, и может серьезно повлиять на характеристики (Существует много примеров, посмотрите here, here, чтобы получить представление). Это должно быть Последнее решение для курорта только для компонента, показанного один раз в то время.

public class BalloonView extends TextView { 

    protected NinePatchDrawable bg; 
    protected Paint paint; 
    protected Rect padding = new Rect(); 
    protected Bitmap bmp; 

    public BalloonView(Context context) { 
    super(context); 
init(); 
    } 

    public BalloonView(Context context, AttributeSet attrs) { 
    super(context, attrs); 
init(); 
    } 

    public BalloonView(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 
    init(); 
    } 

    @SuppressLint("NewApi") 
    protected void init() { 
    // decode the 9patch drawable 
    bg = (NinePatchDrawable) getResources().getDrawable(R.drawable.balloon); 

    // get paddings from the 9patch and apply them to the View 
    bg.getPadding(padding); 
    setPadding(padding.left, padding.top, padding.right, padding.bottom); 

    // prepare the Paint to use below 
    paint = new Paint(); 
    paint.setAntiAlias(true); 
    paint.setColor(Color.rgb(255,255,255)); 
    paint.setStyle(Style.FILL); 

    // this check is needed in order to get this code 
    // working if target SDK>=11 
    if(Build.VERSION.SDK_INT >= 11) 
     setLayerType(View.LAYER_TYPE_SOFTWARE, paint); 

    // set the shadowLayer 
    paint.setShadowLayer(
     padding.left * .2f, // radius 
     0f, // blurX 
     padding.left * .1f, // blurY 
     Color.argb(128, 0, 0, 0) // shadow color 
    ); 
    } 

    @Override 
    protected void onDraw(Canvas canvas) { 
    int w = getMeasuredWidth(); 
    int h = getMeasuredHeight(); 

    // set 9patch bounds according to view measurement 
    // NOTE: if not set, the drawable will not be drawn 
    bg.setBounds(0, 0, w, h); 

    // this code looks expensive: let's do once 
    if(bmp == null) { 

     // it seems like shadowLayer doesn't take into account 
     // alpha channel in ARGB_8888 sources... 
     bmp = Bitmap.createBitmap(w, h, Config.ARGB_8888); 

     // draw the given 9patch on the brand new bitmap 
     Canvas tmp = new Canvas(bmp); 
     bg.draw(tmp); 

     // extract only the alpha channel 
     bmp = bmp.extractAlpha(); 
    } 

    // this "alpha mask" has the same shape of the starting 9patch, 
    // but filled in white and **with the dropshadow**!!!! 
    canvas.drawBitmap(bmp, 0, 0, paint); 

    // let's paint the 9patch over... 
    bg.draw(canvas); 

    super.onDraw(canvas); 
    } 
} 

Прежде всего для того, чтобы получить программное отбрасываемой тени вы должны иметь дело с Paint.setShadowLayer(...), как заявил here. В основном вы должны определить теневой слой для объекта Paint, используемый для рисования на Canvas вашего пользовательского вида. К сожалению, вы не можете использовать объект Paint для рисования NinePatchDrawable, поэтому вам нужно преобразовать его в Bitmap (1-й взлом).Кроме того, кажется, что теневые слои не могут нормально работать с изображениями ARGB_8888, поэтому единственным способом, который я нашел для того, чтобы получить правильную тень, было рисование альфа-маски данного NinePatchDrawable (2-го взлома) чуть ниже самого себя.

Вот несколько sshots (проверено на Android [email protected] и [email protected]) enter image description here enter image description here

Edit: просто быть тщательным, я прикрепил 9patch, используемый в тесте (помещенных в res/drawable/mdpi) enter image description here

+0

Спасибо за ответ. Попробую. Но я не согласен с 9Patch. Нерасширенные области 9Patch не масштабируются для каждой плотности экрана, поэтому они будут меньше отображаться на экранах с высокой плотностью - поэтому площадь тени будет меньше, и я не буду знать, где разместить детей контейнера с заполнением – JackMahoney

+2

@JackMahoney извините но вы ошибаетесь. Посмотрите на скриншоты выше: есть 2 разных ** реальных устройства **: 320x480 | mdpi, 720x1280 | xhdpi. В обоих случаях используется один и тот же 9patch. Вы можете измерить круглый угол шара в пикселях в каждом снимке, и вы заметите, что на xhdpi 2x ширина, чем на mdpi. Также соответственно расширяются прокладки (определенные в 9patch). –

+0

@ a.bertucci можете ли вы добавить образец проекта для меня? – linker

3

Попробуйте эту технику.

container_dropshadow.xml

`

<!-- Drop Shadow Stack --> 
<item> 
    <shape> 
     <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" /> 
     <solid android:color="#00CCCCCC" /> 
    </shape> 
</item> 
<item> 
    <shape> 
     <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" /> 
     <solid android:color="#10CCCCCC" /> 
    </shape> 
</item> 
<item> 
    <shape> 
     <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" /> 
     <solid android:color="#20CCCCCC" /> 
    </shape> 
</item> 
<item> 
    <shape> 
     <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" /> 
     <solid android:color="#30CCCCCC" /> 
    </shape> 
</item> 
<item> 
    <shape> 
     <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" /> 
     <solid android:color="#50CCCCCC" /> 
    </shape> 
</item> 

<!-- Background --> 
<item> 
<shape> 
     <solid android:color="@color/white" /> 
    <corners android:radius="3dp" /> 
</shape> 
</item> 

Затем вы можете применить его к компоновке XML в качестве фона, как

LinearLayout андроида: фон =" @ drawable/container_dropshadow "

`

+0

Хм, это выглядит немного нерешительным. Можете ли вы объяснить, какова идея этого. Похоже, вы только что собрали серию коробок друг на друга. Это не создаст градиент. – JackMahoney

+1

Я думаю, что проблема в твоей стороне, Майкл также предложил одну логику. Но вы сказали, пожалуйста, подробно. Я также показал способ.Но вы сказали то же самое, пожалуйста, объясните. Сначала у вас есть поиск в google. Много материала есть. если вы не можете найти точное решение, переходите к переполнению стека, а затем поднимаете вопрос. Мы не можем отправить полный фрагмент, чтобы выполнить вашу работу. Спасибо за понимание. Все самое лучшее :) – AndroidEnthusiastic

+0

Ответ неправильный. Прокладка для каждого элемента одинакова, поэтому он создает сплошной цвет, а не градиент. – JackMahoney

3

Это самый простой способ падающая тень на любую вещь, либо расположение, кнопки и т.д. background.xml

<?xml version="1.0" encoding="utf-8"?> 
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android"> 
     <item > 
      <shape 
       android:shape="rectangle"> 
      <solid android:color="@android:color/darker_gray" /> 
      <corners android:radius="5dp"/> 
      </shape> 
     </item> 
     <item android:right="1dp" android:left="1dp" android:bottom="2dp"> 
      <shape 
       android:shape="rectangle"> 
      <solid android:color="@android:color/white"/> 
      <corners android:radius="5dp"/> 
      </shape> 
     </item> 
    </layer-list> 

в вашем main.xml

<LineaLayout 
android:layout_width="wrap_content" 
android:layout_height="wrap_content" 
android:background="@drawable/background" 
/> 
+0

понравилось это решение! с небольшим изменением на (см. http://www.google.com/design/spec/style/color.html#color-color-palette) и < item android: bottom = "2dp"> Я мог бы имитировать кнопку Google Play. – Milanor

+0

это также не создает градиент, только сплошной цвет. –