Search results for 'Firemonkey/UI / UX / 디자인'

TListView Dynamic Appearance로 추가한 항목을 코드로 제어하기

2016.09.20 15:16

(해당 기능은 RAD 스튜디오 10.1 베를린 이상에서 사용할 수 있습니다.)


TListView는 이미 정의된 ItemAppreance를 이용해 목록을 구성하기 때문에 TListBox에 비해 스크롤이 부드럽고 빠릅니다.


하지만, 베를린 버전에서 Dynamic appreance 기능이 추가되어 아래 그림과 같이 아이템 항목을 마음대로 정의할 수 있습니다.



Dynamic Appreance에 대한 자세한 내용은 아래 링크를 참고하세요.

오늘 소개할 내용은 위와 같이 사용자가 추가한 항목을 코드를 통해 제어하는 예제입니다.


아래 코드는 ListView1에서 Text2 아이템을 찾아 글자색을 녹색으로, 글자 스타일을 Bold, Italic으로 설정합니다.

procedure TForm2.Button3Click(Sender: TObject);
  function GetTextItem2: TCustomTextObjectAppearance;
  var
    Obj: TCommonObjectAppearance;
  begin
    Result := nil;
    for Obj in ListView1.ItemAppearanceObjects.ItemObjects.Objects do
    begin
      if not (Obj is TCustomTextObjectAppearance) then
        Continue;

      if Obj.Name = 'Text2' then
      begin
        Result := Obj as TCustomTextObjectAppearance;
        Break;
      end;
    end;
  end;
var
  Text2: TCustomTextObjectAppearance;
begin
  // Text2 항목 찾기
  Text2 := GetTextItem2;

  if not Assigned(Text2) then
    Exit;

  Text2.TextColor := TAlphaColorRec.Green;
  Text2.Font.Style := [TFontStyle.fsBold, TFontStyle.fsItalic];
//  Text2.re
end;


아래 코드는 ListView의 크기가 변경된 경우(TListView.OnResize) 내부 항목들의 위치와 너비를 일괄조정하는 샘플입니다.

procedure TForm2.ListView1Resize(Sender: TObject);
var
  L, R, W, Ratio: Single;
  Obj: TCommonObjectAppearance;
begin
  W := ListView1.Width - ListView1.ItemSpaces.Left - ListView1.ItemSpaces.Right;
  L := W;
  R := 0;

  for Obj in ListView1.ItemAppearanceObjects.ItemObjects.Objects do
  begin
    // 가장 왼쪽의 이미지 제외
    if Obj is TCustomImageObjectAppearance then
      Continue;

    // 가장 왼쪽 위치
    if Obj.PlaceOffset.X < L then
      L := Obj.PlaceOffset.X;
    // 가장 오른쪽 위치
    if (Obj.PlaceOffset.X + Obj.Width) > R then
      R := Obj.PlaceOffset.X + Obj.Width;
  end;

  Ratio := (W-L) / (R-L); // (변경된 너비 / 원래너비)
  for Obj in ListView1.ItemAppearanceObjects.ItemObjects.Objects do
  begin
//    if Obj.PlaceOffset.X < L then
    if Obj is TCustomImageObjectAppearance then
      Continue;

    Obj.PlaceOffset.X := ((Obj.PlaceOffset.X - L)  * Ratio) + L;
    Obj.Width := Obj.Width * Ratio;
  end;
end;


샘플 프로젝트

ListViewDynamicAppearance.zip


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인

  1. Blog Icon
    채종일

    안녕하세요. 해당 기능을 사용하려고 하는데

    라이브 바인딩이 아니라 직접 소스코드로 항목을 추가하려면 어떻게 해야할까요??

[XE8] 스피드버튼의 이미지를 크게 표시할 수 있습니다.

2015.06.04 10:32

파이어몽키에 이미지리스트가 추가(XE8~)되어 스피드버튼에 이미지 표시가 아주 편리해졌습니다.


하지만, 이미지의 기본크기가 너무 작다고 느끼신 경우 다음 글을 통해 스타일을 수정해 큼지막한 이미지를 표시해 보세요.



중국인 개발자(龟山阿卍)의 블로그입니다. 


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인 Imagelist, Speedbutton, XE8

[FMX] ListBox 전체체크 기능 구현하기

2015.03.04 13:28

리스트박스(TListBox)에 체크박스를 두고 버튼 클릭 시 전체체크하는 기능에 대한 문의가 있어 정리해 봤습니다.


이 글에서는 아래 내용을 학습할 수 있습니다.

  • 리스트박스를 동적으로 생성하는 방법
  • 리스트박스 아이템에 체크박스를 표시하는 속성

화면 구성

컴포넌트 속성 설정

주요 속성을 설정합니다.(Object Inspector에서 속성을 설정해도 됩니다.)

procedure TForm2.FormCreate(Sender: TObject);
begin
  Button1.StaysPressed := True; //버튼 눌림(IsPressed) 상태 유지

  ListBox1.DefaultItemStyles.ItemStyle := 'listboxitemrightdetail'; // 텍스트와 오른쪽에 상세정보가 표시되도록 기본속성 지정
  ListBox1.ShowCheckboxes := True; // 리스트박스 항목에 체크박스가 표시되도록 설정
end;

새로고침

procedure TForm2.Button2Click(Sender: TObject);
var
  I: Integer;
  Item: TListBoxItem;
begin
  ListBox1.Items.Clear;
  ListBox1.BeginUpdate;
  try
    for I := 0 to 10 do
    begin
      Item := TListBoxItem.Create(ListBox1);
      Item.Parent := ListBox1;
      Item.Text := 'Item ' + I.ToString;
      Item.ItemData.Detail := 'Description';
    end;
  finally
    ListBox1.EndUpdate;
  end;
end;

전체선택

procedure TForm2.Button1Click(Sender: TObject);
var
  I, Idx: Integer;
begin
  Idx := ListBox1.ItemIndex;
  ListBox1.BeginUpdate;
  try
    for I := 0 to ListBox1.Items.Count - 1 do
    begin
      ListBox1.ListItems[I].IsChecked := Button1.IsPressed;
    end;
  finally
    ListBox1.EndUpdate;
    ListBox1.ItemIndex := Idx;
  end;
end;

소스코드

ProjectLBCheck.zip


Tips>

커스텀 스타일로 TListBoxItem 스타일을 만드는 경우 TCheckBox의 StyleName을 "check"로 지정하기 바랍니다.


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인

태블릿용 멀티컬럼 리스트뷰 ItemAppearace 만들기

2015.02.09 11:28

모바일(폰과 패드)용 목록을 만드는 컴포넌트는 대표적으로 ListView와 ListBox가 있습니다.

두 목록 컴포넌트의 차이점은 이름으로 알수 있듯이 목적에 차이가 있습니다.


ListView는 

View 즉 보여주는 것을 목적으로 하기 때문에 목록을 빠르게 이동할 수 있지만 목록아이템을 꾸미는데 제한적입니다.

반면, 

ListBox는 

Box 즉 목록 아이템에 다른 아이템을 담아 자유롭게 목록을 구성할 수 있는 컴포넌트입니다. 목록을 원하는데로 꾸밀 수 있지만 많은 컴포넌트를 담는다면 스크롤이 상대적으로 느려질 수 있습니다.


두 목록 컴포넌트의 목적을 잘 이해하고 사용하시기 바랍니다.


ListView는 목록 아이템을 꾸미는데 제한적이라고 했는데요. 그 이유는 TListViewItem은 (TFMXObject를 상속받지 않았기 때문에)다른 컴포넌트를 올릴 수 없도록 설계되었습니다. 아이템 외관을 꾸미기 위해서는 ListView의 ItemAppearance 속성을 이용할 수 있습니다. 이 속성은 기본으로 7개의 항목만 제공됩니다.

기본 제공되는 ItemAppearance는 모바일 폰(작은화면)을 기준으로 제공합니다. 태블릿용 앱에서는 화면이 다소 허전해 질 수 있습니다.

이렇게 기본 제공되는 외관을 변경(항목 추가, 위치 이동)하기 위해서는 ItemAppearance 패키지 프로젝트를 직접 만들어 설치(Install) 후 사용할 수 있습니다.

ItemAppearance 패키지 제작

기본 샘플 참고

델파이 기본 샘플에서 ListView 샘플(Samples\Object Pascal\Mobile Samples\User Interface\ListView)을 참고할 수 있습니다.

ListView 샘플에는 상세정보를 여러건 보여주는 MultiDetailItem, 별점을 표시하는 RatingItem 등의 목록을 구성할 수 있는 패키지 프로젝트(*.dpk)가 있고, 그 패키지를 사용하는 샘플 프로젝트가 있습니다.

먼저 패키지 프로젝트를 열고 프로젝트 매니저에서 설치(오른쪽 마우스 > Install) 후 샘플 프로젝트를 열어 확인하기 바랍니다.

패키지 프로젝트를 열면 ItemAppearance 속성에 새로운 항목이 추가된 것을 볼 수 있습니다.

MultiDetailHorzItem

제가 직접 4개의 열을 갖는 ItemAppearance를 만들어 테스트 해 봤습니다. 기본 제공되는 MultiDetailItem을 수정했습니다.

확실히 ListBox, Grid, StringGrid에 비해 목록을 스크롤하는 속도가 빠릅니다.


unit MultiDetailHorzAppearanceU;

interface

uses FMX.ListView, FMX.ListView.Types, System.Classes, System.SysUtils,
FMX.Types, System.UITypes, FMX.MobilePreview;

type

  TMultiDetailHorzAppearanceNames = class
  public const
    ListItem = 'MultiDetailHorzItem';
    ListItemCheck = ListItem + 'ShowCheck';
    ListItemDelete = ListItem + 'Delete';
    Detail1 = 'det1';  // Name of MultiDetail object/data
    Detail2 = 'det2';
    Detail3 = 'det3';
  end;

implementation

uses System.Math, System.Rtti;

type

  TMultiDetailHorzItemAppearance = class(TPresetItemObjects)
  public const
    cTextMarginAccessory = 8;
    cDefaultHeight = 40;
  private
    FMultiDetail1: TTextObjectAppearance;
    FMultiDetail2: TTextObjectAppearance;
    FMultiDetail3: TTextObjectAppearance;
    procedure SetMultiDetail1(const Value: TTextObjectAppearance);
    procedure SetMultiDetail2(const Value: TTextObjectAppearance);
    procedure SetMultiDetail3(const Value: TTextObjectAppearance);
  protected
    function DefaultHeight: Integer; override;
    procedure UpdateSizes; override;
    function GetGroupClass: TPresetItemObjects.TGroupClass; override;
    procedure SetObjectData(const AListViewItem: TListViewItem; const AIndex: string; const AValue: TValue; var AHandled: Boolean); override;
  public
    constructor Create; override;
    destructor Destroy; override;
  published
    property MultiDetail1: TTextObjectAppearance read FMultiDetail1 write SetMultiDetail1;
    property MultiDetail2: TTextObjectAppearance read FMultiDetail2 write SetMultiDetail2;
    property MultiDetail3: TTextObjectAppearance read FMultiDetail3 write SetMultiDetail3;
    property Accessory;
  end;

  TMultiDetailHorzDeleteAppearance = class(TMultiDetailHorzItemAppearance)
  private const
    cDefaultGlyph = TGlyphButtonType.Delete;
  public
    constructor Create; override;
  published
    property GlyphButton;
  end;

  TMultiDetailShowCheckAppearance = class(TMultiDetailHorzItemAppearance)
  private const
    cDefaultGlyph = TGlyphButtonType.Checkbox;
  public
    constructor Create; override;
  published
    property GlyphButton;
  end;

const
  cMultiDetail1Member = 'Detail1';
  cMultiDetail2Member = 'Detail2';
  cMultiDetail3Member = 'Detail3';

constructor TMultiDetailHorzItemAppearance.Create;
begin
  inherited;
  Accessory.DefaultValues.AccessoryType := TAccessoryType.More;
  Accessory.DefaultValues.Visible := True;
  Accessory.RestoreDefaults;
  Text.DefaultValues.VertAlign := TListItemAlign.Trailing;
  Text.DefaultValues.TextVertAlign := TTextAlign.Center;
  Text.DefaultValues.Visible := True;
  Text.RestoreDefaults;

  FMultiDetail1 := TTextObjectAppearance.Create;
  FMultiDetail1.Name := TMultiDetailHorzAppearanceNames.Detail1;
  FMultiDetail1.DefaultValues.Assign(Text.DefaultValues);  // Start with same defaults as Text object
  FMultiDetail1.DefaultValues.IsDetailText := True; // Use detail font
  FMultiDetail1.VertAlign := TListItemAlign.Leading;
  FMultiDetail1.Align := TListItemAlign.Trailing;
  FMultiDetail1.TextVertAlign := TTextAlign.Center;
  FMultiDetail1.RestoreDefaults;
  FMultiDetail1.OnChange := Self.ItemPropertyChange;
  FMultiDetail1.Owner := Self;

  FMultiDetail2 := TTextObjectAppearance.Create;
  FMultiDetail2.Name := TMultiDetailHorzAppearanceNames.Detail2;
  FMultiDetail2.DefaultValues.Assign(FMultiDetail1.DefaultValues);  // Start with same defaults as Text object
  FMultiDetail2.VertAlign := TListItemAlign.Leading;
  FMultiDetail2.Align := TListItemAlign.Trailing;
  FMultiDetail2.TextVertAlign := TTextAlign.Center;
  FMultiDetail2.RestoreDefaults;
  FMultiDetail2.OnChange := Self.ItemPropertyChange;
  FMultiDetail2.Owner := Self;

  FMultiDetail3 := TTextObjectAppearance.Create;
  FMultiDetail3.Name := TMultiDetailHorzAppearanceNames.Detail3;
  FMultiDetail3.DefaultValues.Assign(FMultiDetail2.DefaultValues);  // Start with same defaults as Text object
//  FMultiDetail3.DefaultValues.Height := 20; // Move text down
  FMultiDetail3.VertAlign := TListItemAlign.Leading;
  FMultiDetail3.Align := TListItemAlign.Trailing;
  FMultiDetail3.TextVertAlign := TTextAlign.Center;
  FMultiDetail3.RestoreDefaults;
  FMultiDetail3.OnChange := Self.ItemPropertyChange;
  FMultiDetail3.Owner := Self;

  // Define livebindings members that make up MultiDetail
  FMultiDetail1.DataMembers :=
    TObjectAppearance.TDataMembers.Create(
      TObjectAppearance.TDataMember.Create(
        cMultiDetail1Member, // Displayed by LiveBindings
        Format('Data["%s"]', [TMultiDetailHorzAppearanceNames.Detail1])));   // Expression to access value from TListViewItem
  FMultiDetail2.DataMembers :=
    TObjectAppearance.TDataMembers.Create(
      TObjectAppearance.TDataMember.Create(
        cMultiDetail2Member, // Displayed by LiveBindings
        Format('Data["%s"]', [TMultiDetailHorzAppearanceNames.Detail2])));   // Expression to access value from TListViewItem
  FMultiDetail3.DataMembers :=
    TObjectAppearance.TDataMembers.Create(
      TObjectAppearance.TDataMember.Create(
        cMultiDetail3Member, // Displayed by LiveBindings
        Format('Data["%s"]', [TMultiDetailHorzAppearanceNames.Detail3])));   // Expression to access value from TListViewItem

  GlyphButton.DefaultValues.VertAlign := TListItemAlign.Center;
  GlyphButton.RestoreDefaults;

  // Define the appearance objects
  AddObject(Text, True);
  AddObject(MultiDetail1, True);
  AddObject(MultiDetail2, True);
  AddObject(MultiDetail3, True);
  AddObject(Image, True);
  AddObject(Accessory, True);
  AddObject(GlyphButton, IsItemEdit);  // GlyphButton is only visible when in edit mode
end;

constructor TMultiDetailHorzDeleteAppearance.Create;
begin
  inherited;
  GlyphButton.DefaultValues.ButtonType := cDefaultGlyph;
  GlyphButton.DefaultValues.Visible := True;
  GlyphButton.RestoreDefaults;
end;

constructor TMultiDetailShowCheckAppearance.Create;
begin
  inherited;
  GlyphButton.DefaultValues.ButtonType := cDefaultGlyph;
  GlyphButton.DefaultValues.Visible := True;
  GlyphButton.RestoreDefaults;
end;

function TMultiDetailHorzItemAppearance.DefaultHeight: Integer;
begin
  Result := cDefaultHeight;
end;

destructor TMultiDetailHorzItemAppearance.Destroy;
begin
  FMultiDetail1.Free;
  FMultiDetail2.Free;
  FMultiDetail3.Free;
  inherited;
end;

procedure TMultiDetailHorzItemAppearance.SetMultiDetail1(
  const Value: TTextObjectAppearance);
begin
  FMultiDetail1.Assign(Value);
end;

procedure TMultiDetailHorzItemAppearance.SetMultiDetail2(
  const Value: TTextObjectAppearance);
begin
  FMultiDetail2.Assign(Value);
end;

procedure TMultiDetailHorzItemAppearance.SetMultiDetail3(
  const Value: TTextObjectAppearance);
begin
  FMultiDetail3.Assign(Value);
end;

procedure TMultiDetailHorzItemAppearance.SetObjectData(
  const AListViewItem: TListViewItem; const AIndex: string;
  const AValue: TValue; var AHandled: Boolean);
begin
  inherited;

end;

function TMultiDetailHorzItemAppearance.GetGroupClass: TPresetItemObjects.TGroupClass;
begin
  Result := TMultiDetailHorzItemAppearance;
end;

procedure TMultiDetailHorzItemAppearance.UpdateSizes;
const
    // Total Rate = 1.0
    TextWidthRate = 0.4;
    Det1WidthRate = 0.2;
    Det2WidthRate = 0.2;
    Det3WidthRate = 0.2;

var
  LOuterHeight: Single;
  LOuterWidth: Single;
  LInternalWidth: Single;
  LImagePlaceOffset: Single;
  LImageTextPlaceOffset: Single;
begin
  BeginUpdate;
  try
    inherited;

    // Update the widths and positions of renderening objects within a TListViewItem
    LOuterHeight := Height - Owner.ItemSpaces.Top - Owner.ItemSpaces.Bottom;
    LOuterWidth := Owner.Width - Owner.ItemSpaces.Left - Owner.ItemSpaces.Right;
    Text.InternalPlaceOffset.X :=
      Image.ActualPlaceOffset.X +  Image.ActualWidth + LImageTextPlaceOffset;

    LInternalWidth := (LOuterWidth - Text.ActualPlaceOffset.X - Accessory.ActualWidth);
    if Accessory.ActualWidth > 0 then
      LInternalWidth := LInternalWidth - cTextMarginAccessory;
    Text.InternalWidth := Max(1, LInternalWidth * TextWidthRate);

    MultiDetail1.InternalWidth := LInternalWidth * Det1WidthRate;
    MultiDetail1.InternalPlaceOffset.X := Text.InternalPlaceOffset.X + Text.InternalWidth;
    MultiDetail2.InternalWidth := LInternalWidth * Det2WidthRate;
    MultiDetail2.InternalPlaceOffset.X := MultiDetail1.InternalPlaceOffset.X + MultiDetail1.InternalWidth;
    MultiDetail3.InternalWidth := LInternalWidth * Det3WidthRate;
    MultiDetail3.InternalPlaceOffset.X := MultiDetail2.InternalPlaceOffset.X + MultiDetail2.InternalWidth;
  finally
    EndUpdate;
  end;
end;

type
  TOption = TCustomListView.TRegisterAppearanceOption;
const
  sThisUnit = 'MultiDetailHorzAppearanceU';     // Will be added to the uses list when appearance is used
initialization
  // MultiDetailItem group
  TCustomListView.RegisterAppearance(
    TMultiDetailHorzItemAppearance, TMultiDetailHorzAppearanceNames.ListItem,
    [TOption.Item], sThisUnit);
  TCustomListView.RegisterAppearance(
    TMultiDetailHorzDeleteAppearance, TMultiDetailHorzAppearanceNames.ListItemDelete,
    [TOption.ItemEdit], sThisUnit);
  TCustomListView.RegisterAppearance(
    TMultiDetailShowCheckAppearance, TMultiDetailHorzAppearanceNames.ListItemCheck,
    [TOption.ItemEdit], sThisUnit);
finalization
  TCustomListView.UnregisterAppearances(
    TArray.Create(
      TMultiDetailHorzItemAppearance, TMultiDetailHorzDeleteAppearance,
      TMultiDetailShowCheckAppearance));
end.



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인

  1. Blog Icon
    고재학

    소스 코드에 버그가 있는것 같네요.
    230 Text.InternalPlaceOffset.X :=
    231 Image.ActualPlaceOffset.X + Image.ActualWidth + LImageTextPlaceOffset;


  2. 어떤 증상이 있죠? 좀 더 자세히 알려주시면 감사하겠습니다.^^

  3. Blog Icon
    고재학

    윈도우에서 테스트할 경우에는 문제가 없었는데 모바일에서 컴파일하면 내용을 표시하는 과정에서 "Invalid floating point operation"가 나타납니다.
    위 코드를 제거하고 테스트해보니 정상작동 하였습니다.

  4. Blog Icon
    고재학

    질문에 답변도 해주시는지요?
    위의 예제를 응용하여 리스트뷰를 만들었는데... MultiDetail1에 대입되는 값에 따라 글자색을 변경하고 싶습니다.

    예를 들어
    MultiDetail1의 값이 1이면 MultiDetail1의 글자색은 빨강색
    MultiDetail1의 값이 2이면 MultiDetail1의 글자색은 파랑색

    모바일앱에서 MultiDetail1의 TextColor를 어떻게 동적으로 변경할 수 있을까요?

  5. C:\Users\Public\Documents\Embarcadero\Studio\17.0\Samples\Object Pascal\Multi-Device Samples\User Interface\ListView\ListViewMultiDetailAppearance

    위 ListView 샘플에 다음 코드로 진행해 봤습니다.(잘되네요^^)

    // ListView의 OnUpdateObjects 이벤트에 아래 코드 구현
    procedure TForm594.ListViewMultiDetailUpdateObjects(const Sender: TObject;
    const AItem: TListViewItem);
    var
    Textitem: TListItemText;
    begin
    Textitem := AItem.View.FindDrawable(TMultiDetailAppearanceNames.Detail1) as TListItemText;
    if Assigned(TextItem) then
    begin
    // M으로 시작하는 text 빨간색으로 표시
    if Textitem.Text.StartsWith('M') then
    begin
    TextItem.TextColor := $FFFF0000;
    end;
    end;
    end;

    위 디렉토리의 샘플을 참고하시면 다양한 기능을 구현할 수 있습니다.

  6. Blog Icon
    고재학

    와~~~ 멋지네요.
    XE8에서 작동하지않아 Seattle로 업그레이드하니 잘 됩니다.
    감사합니다.

[XE7] 안드로이드 L 스타일팩 다운로드

2015.01.23 11:07

최근 안드로이드 L(롤리팝: 5.0)을 적용한 스마트폰이 나오고 있습니다.


엠바카데로에서도 안드로이드 5.0 지원을 위해 핫픽스를 배포하고 안드로이드 L에 맞는 스타일팩을 배포합니다.


안드로이드 L 스타일팩 활용방법은 Sarina DuPont의 글을 참고하기 바랍니다.

관련 글



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인 XE7, 롤리팝, 스타일, 안드로이드, 안드로이드 L

파이어몽키 커스텀 스타일 제작 따라하기

2014.11.03 14:56

파이어몽키에서는 스타일 기반으로 아주 유연하고 다양한 외관의 앱을 개발할 수 있습니다.


오늘은 엠바카데로의 블로그에 소개된 파이어몽키 커스텀 스타일을 제작하는 방법을 소개합니다.

총 9단계에 걸처 파이어몽키 스타일을 수정(제작)할 수 있습니다. 




관련글



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인

  1. Blog Icon
    궁금이

    저 궁금한게 있는데요 윈도에서 스타일 적용했을때 잔상이 남던데 어떻게 해결하시나요

멀티 디바이스 디자이너 소개

2014.09.17 10:23

최근 다양한 스마트 기기가 속속 등장하고 있습니다.


안드로이드폰의 다양성은 수많은 제조사의 숫자가 대변하구요. 아이폰의 경우도 전통적인 작은화면(3.5인치, 4인치)의 벗어나 이제 큰화면(4.7인치, 5.5인치) 출시되고 있습니다. 그뿐아니라 다양한 웨어러블 디바이스와 태블릿은 또 스마트폰과 다른 화면과 스타일을 갖습니다.


이렇듯 다양한 스마트기기의 종류만큼 다양한 화면크기와 스타일도 제각각입니다.

이런 디바이스의 특성에 맞는 화면과 스타일을 맞추는 것은 멀티 플랫폼 제품을 만드는 개발자와 디자이너에게 매우 큰 고민일 것입니다.


이러한 고민을 RAD Studio에서 풀어나가기 위해 "멀티 디바이스 디자이너"라는 새로운 컨셉의 개발화면이 추가되었습니다.

멀티 디바이스 디자이너는 RAD Studio XE7(Delphi XE7, C++Builder XE7)에 추가된 기능입니다.

멀티 디바이스 디자이너



멀티 디바이스 디자이너는 디바이스 별로 상이한 화면크기와 스타일을 각각의 디바이스에 맞도록 화면 구성하기 위한 새로운 화면 디자인 기술입니다.


안드로이드 4인치의 레이아웃과 7인치의 레이아웃을 각각 다르게 구성할 수 있고, 아이폰의 화면도 그와 다르게 구성할 수 있도록 해줍니다.


멀티 디바이스 디자이너는 "마스터 뷰"와 "디바이스 뷰" 개념이 적용되어, 클래스와 같이 화면을 상속받아 개발하는 컨셉입니다. 마스터 뷰에서 기본적이고 공통적인 화면을 구성하고 각각의 디바이스 뷰를 추가해 디바이스에 최적화된 화면으로 변경할 수 있습니다.

마스터 뷰의 컨트롤과 레이아웃은 디바이스 뷰에서 상속받기 때문에 다양한 디바이스 뷰는 같은 레이아웃에서 시작하고, 디바이스 뷰에서 레이아웃을 변경하면 변경된 부분만 디바이스 뷰에 적용됩니다.

❑ 마스터뷰와 디바이스뷰

마스터 뷰

마스터 뷰에 Button과 StyleBook을 올려놓았습니다. 버튼의 텍스트도 변경했습니다.

컴포넌트와 컴포넌트의 속성은 모두 디바이스뷰에 상속됩니다.

이후 안드로이드와 iOS에서 버튼의 위치와 스타일을 각각의 디바이스에 맞게 변경합니다.

안드로이드 4"(디바이스 뷰)

iPhone 4"(디바이스 뷰)


❑ 디바이스 별 화면을 하나의 소스코드로 제어

위와 같이 디바이스 별로 화면을 구성하면 아래와 같이 마스터 뷰와 디바이스 뷰 별 폼파일(*.fmx)이 생성됩니다.



그리고 컴파일 및 빌드 시 선택한 플랫폼에 맞는 폼파일을 이용해 앱이 만들어지기 때문에 플랫폼과 디바이스에 맞는 화면의 앱을 제작할 수 있습니다.

마무리

이제는 클래스 뿐 아니라 화면디자인까지 상속받아 개발할 수 있습니다.

새로운 멀티 디바이스 디자이너를 이용해 다양한 디바이스에 맞는 화면을 제공해 보시기 바랍니다.


RAD Studio(델파이, C++빌더)란?

Embarcadero® RAD Studio는 윈도우, 맥, iOS, 안드로이드, 웨어러블 앱을 한번에 개발하고 다양한 기기들과 각종 센서를 서로연결하는 애플리케이션을 매우 빠르게 개발할 수 있는 완벽한 소프트웨어 개발 도구입니다. (자세히)

관련글



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인

다양한 형태로 표시되는 적응형 컴포넌트 TMultiView 소개

2014.09.17 09:51

이번 글에서는 XE7에서 새로 추가된 멀티뷰(TMultiView) 컴포넌트에 대해 간단히 살펴봅니다.

멀티뷰(TMultiView)


멀티뷰는 하나의 컴포넌트가 4개의 다양한 형태로 표시할 수 있는 적응형 컴포넌트입니다.

"Mode" 속성을 변경해 원하는 표시방식을 직접 설정할 수 있지만 PlatformBehaviour 속성을 선택하면 플랫폼과 디바이스의 화면 해상도에 따라 적절한 표시방식으로 자동으로 화면에 표시됩니다.



표시방식은 아래와 같습니다.

TMultiView.ModeMaster Pane Presentation
DrawerDrawer (Push/Overlap)
PanelDocked panel
PlatformBehaviour(see the following table)
PopoverPopup menu
CustomFor details, see the Custom Mode subsection.


멀티뷰는 RAD Studio XE7(Delphi XE7, C++Builder XE7)에 추가된 기능입니다.

참고자료




저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인 TMultiView, XE7

멀티 디바이스 디자이너에 사용자 정의 View를 추가하기

2014.09.15 11:16

RAD Studio XE7에는 플랫폼과 디바이스의 폼팩터 별로 최적화된 화면을 구성할 수 있는 멀티 디바이스 디자이너 기술이 추가되었습니다.


이제는 스마트폰의 화면구성과 태블릿의 화면구성을 아주 쉽게 차별화해 분리할 수 있게 되었습니다.



위에는 기본적으로 제공되는 View의 목록 입니다. 최근 출시된 대부분의 스마트폰과 태블릿이 포함되어 있습니다.


만약, 위 목록에 없는 장치의 화면을 별도록 구성하기 위해서는 여러분들이 직접 폼팩터와 플랫폼을 지정해 사용자 정의 뷰(Cusotom View)를 추가할 수 있습니다.


패키지를 생성해 추가하는 방식인데요. 아래 링크를 참고하시면 어렵지 않게 추가할 수 있습니다.



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인 Rad Studio, XE7, 멀티디바이스디자이너

  1. Blog Icon
    오실장

    이것 문서대로 테스트 해봤는데 마지막단계에서 MobileDevices.xml 파일을 수정하고 델파이 IDE를 재실행하니 MobileDevices.xml 파일이 저절로 다시 원상복구 되어서 사용자 Device View 가 생성되지 않더군요.
    시간될때 확인한번 해주세요.

원형 프로세스 바 - ProgressCircle(소스공개)

2014.07.04 09:17

중국인(?) 델파이어가 공개한 원형 프로그레스 바를 아주 심플하게 만든 소스코드(ProgressCircle)가 있어 공개합니다.



TArc, TCircle과 TText 3개를 이용해 구현되었습니다.

아래 코드와 같이 이용할 수 있습니다. 

컴포넌트로 만들어서 사용하면 아주 좋은 UI에 도움이 될 것 같습니다.



소스코드



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인 ProgressCircle, User Interface, 소스공개

Home Screen Navigation - UI Design 참고

2014.06.19 10:11

앱의 메인화면을 아이콘을 활용해 멋지게 구성하는 내용을 소개합니다.


아래와 같이 구성하는 내용인데요.

아이콘들의 구성을 위해서 TGridPanelLayout 컴포넌트를 이용하고, 

아이콘은 ImageControl을 이용해 구성됩니다.


자세한 내용은 아래 링크를 통해 확인하세요.

http://blogs.embarcadero.com/sarinadupont/2014/06/16/mobile-user-interface-design-home-screen-navigation/



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인 UI

당겨서 새로고침(Pull to refresh) - 안드로이드, iOS 구현

2014.06.19 10:00

이전에 iOS용 당겨서 새로고침 글을 소개해 드렸는데요.

안드로이드에서 안된다는 말씀이 있어 찾아보니 Android와 IOS를 동시에 사용할 수 있는 내용이 있어 소개해 드립니다.


첫번째는, 

유튜브를 통해 찾은 내용입니다.

당겨서 새로고침을 구현하는 내용을 동영상을 통해 안내합니다.

UI 구성 뿐 아니라 데이터를 가져오는 부분까지 설명을 해줍니다.

단, 이야기하는 언어가 한국어/영어가 아니므로, 화면만 보시며 따라하셔야 합니다.



두번째는 이전에 소개해 드린 iOS용 당겨서 새로고침을 안드로이드에서도 사용할 수 있도록 수정한 내용입니다.

http://www.fmxexpress.com/swipe-down-to-refresh-listview-with-delphi-xe5-firemonkey-on-android-and-ios/

하단의 안드로이드용 소스코드를 다운받으셔서 기능을 추가하시면 됩니다.




저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인 android, ios, pull to refresh, UI

  1. Blog Icon
    gg..

    안녕하세요. 델파이 xe 5 를 사용하고 있는 유저인데요.
    sdk 를 업데이트하고 usb 디버깅을 사용해도,
    타겟에 핸드폰이 인식이 안되는건 대체 무슨 증상일까요..ㅠㅠㅠ

ListView의 Header를 첫글자로 정렬하는 방법

2014.06.05 09:49
TListView에서 첫 글자로 정렬하는 팁이 엠바카데로 커뮤니티에 올라왔습니다.

핵심은 라이브 바인딩 시 CustomFormat에서 SubString을 이용하는 것인데요.
이번은 Header를 통한 정렬이기 때문에 FillHeaderCustomFormat에서 SubString으로 첫 글자를 분리하여 표시하고 정렬할 수 있습니다.

한글의 경우 ㄱ, ㄴ, ㄷ, ... 순의 정렬이 필요할 것 같은데요. 이 부분은 한국 개발자 분들이 시도해봐야 할 것 같습니다.^^


참고로 엠바카데로 커뮤니티 사이트에 다양한 팀과 기술이 자주 올라오니 자주 방문하셔서 좋은 정보 얻어가시기 바랍니다.


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인

  1. 권순길 님이 한글로 정렬하도록 시도하셨습니다.
    http://www.sicmy.com/wordpress/listview%EC%9D%98-header%EB%A5%BC-%EC%B2%AB%EA%B8%80%EC%9E%90%EB%A1%9C-%EC%A0%95%EB%A0%AC%ED%95%98%EB%8A%94-%EB%B0%A9%EB%B2%95%ED%95%9C%EA%B8%80%EB%B2%84%EC%A0%84/

    좋은 정보가 많이 올라올 것 같은 사이트네요^^ 기대됩니다.
    http://www.sicmy.com/

Instagram Filter 효과와 같은 앱(소스 포함)

2014.04.09 11:21


인스타그램 앱과 같이 사진에 다양한 필터를 적용하는 앱에 대한 소스코드가 공개되 었습니다.

아시다 시피 파이어몽키에는 60여종의 다양한 이펙트(TEffect)가 제공(62개의 파이어몽키 이미지 효과(Effects))됩니다. 이펙트를 이용해 아주 쉽게 위와 같은 동작을 하는 앱을 만들 수 있습니다.


간단한 구현 원리를 추가하면, 

  • 첫번째 탭에서 원본이미지에 TSelection 컴포넌트를 올리고 영역을 지정합니다.
  • 두번째 탭에서 상단의 이미지에 앞에서 선택한 영역의 이미지를 표시하고, 아래에는 다양한 효과(TEffect)가 적용된 이미지를 놓고, 이미지 선택 시 TFilterEffect.ProcessEffect 메소드를 적용합니다.
  • 세번째 탭에서는 이펙트가 적용된 이미지를 ActionList의 ShowShareSheetAction을 통해 공유 및 저장합니다.


더 자세한 내용은 아래의 소스코드를 통해 확인하세요.



참고링크



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인 FMX, TEffect

모바일 Gesture로 메뉴를 표시/감추기(소스공개)

2014.03.27 10:54

제스쳐(손가락 끌기)로 좌측 메뉴를 표시하고 감추는 샘플 동영상 및 코드입니다.






관련링크



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인 Drawer, Gesture, Menu, UI, 모바일, 소스공개

  1. Blog Icon
    관심

    궁금한게 한가지 있습니다.
    XE5로 안드로이드 앱 작업중인데.. main.pas 와 sub.pas 이렇게 두개의 창이 있는데..
    서로 데이터를 주고 받고 싶은데.. 그게 잘 않되더라구요..
    main.pas에서 fromSub.show 해서 창이 띄우고 그곳에서 작업을 해서 해당 결과를 main 창으로 가져오려고
    전역 변수도 써보고 했는데.. 그 데이터가 않들어 오더라구요..
    혹시 방법이 잘못된 것인지요.. 일반 C/S 프로그램만 개발해 봐서리.. 잘 모르겠네요..
    알려주세요.. ^^

  2. 아이디어를 2가지 드릴게요.
    1, SubForm에 이벤트를 통해 내용을 전달할 수 있습니다.
    property OnData: TNotifyEvent read FOnData write FOnData;
    서브폼에서 결과가 나오면 아래의 이벤트를 발생시키고 메인폼에서 서브폼의 public한 데이터를 참고하면 좋을 것 같네요.(너무 어렵나요?)

    2, 모바일에서는 TFrame을 사용하시면 편리합니다.

  3. TSubForm = class(TForm)
    ...
    private
    FData: TMemoryStream;
    procedure DoData;
    public
    property OnData: TNotifyEvent read FOnData write FOnData;
    property Data: TMemoryStream read FData; // 원하시는 데이터 등등...
    end;

    implimentation

    procedure TSubForm.DoData;
    begin
    if Assigned(FonData) then
    FOnData(Self);
    end;

    /////

    TMainForm = ...
    private
    procedure SubFormDataEvent(Sender: TObject);
    end;

    ///
    SubForm := TSubForm.Create(Self);
    SubForm.OnData := SubFormDataEvent;

    //
    procedure TmainForm.SubFormDataEvent(Sender: TObject);
    begin
    if SubForm.Data.Size > 0 then
    begin
    ........
    end;
    end;

    위의 답변이 너무어려워 몇자 구현코드를 추가했는데 이해되실지 모르겠네요 ...-_-; 쉽운 설명은 너무 어려워요...
    대충 흐름만 보시면 될 것 같습니다.

  4. Blog Icon
    관심

    정말 감사합니다..
    함 도전해보구.. 다른 질문 사항 있으면 다시 여쭤 볼께요..
    정말 염치가 없지만 감사합니다..

  5. TFrame을 이용한 내용 곧 포스팅 예정이니 해당 내용도 참고하시면 도움이 될것 같습니다.
    이달안에 포스팅하겠습니다.^^

Sliding Popup menu 디자인(소스 공개)

2014.03.27 10:51



상단의 버튼을 누르면 슬라이드 형태로 팝업메뉴가 나오는 방식입니다. 아래의 동영상으로 자세한 내용 확인할 수 있습니다.





관련링크



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인 Firemonkey, Slide menu, 소스공개

메신저 형태의 대화상자 표시

2014.03.26 09:34


메신저 형태의 대화를 표시하는 방법을 소개합니다.


TVertScrollBox 위에 TCalloutRectangle을 올리고, TText와 TImage로 표현했는데요.

더 다양한 컨트롤들로 확장할 수 있을 것 같습니다.


참고하셔서 더 멋진 UI를 구성하시기 바랍니다.


http://www.fmxexpress.com/build-a-messenger-layout-like-whatsapp-using-delphi-xe5-firemonkey-on-android-and-ios/

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인

웹상의 파일 다운로드 시 진행률 표시하기

2014.02.18 16:10



웹에서 파일을 다운로드 받을때 용량이 큰 경우 화면은 멈추고 사용자는 넋놓고 기다려야 하는데요. 그때 진행률등을 표시하면 너무 좋겠죠? 그 방법을 소개하는 내용이 있습니다. 아래의 링크를 참고하세요.


http://blog.delphiedintorni.it/2013/10/una-form-di-attesa-per-operazioni.html



저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인

파이어몽키에서 끌어서 새로고침 구현

2014.02.11 11:49


3개의 컴포넌트로 간단하게 구성된 끌어서 새로고침 샘플입니다. 쉽게 여러분의 앱에 기능을 추가할 수 있을거에요.

  • TListView
  • TAniIndicator
  • TTimer


자세한 정보는 아래의 링크에서 확인하세요.

http://blogs.embarcadero.com/sarinadupont/2014/02/10/pull-to-refresh-on-ios/

저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인

  1. Blog Icon
    만주

    안녕하세요.
    나무를 심는.... 많은 활동을 하시는것 참고 많아 합니다.
    다름 아니라 fmx 가 멀티플렛폼을 한 소스로 처리한다는것이 장점인데
    아이폰에서는 되고 안드로이드는 따로 처리 해야 한다는것인가요?
    음.....
    개발툴 선택에 고민하다가 xe6 사용하려 맘 굳히고 있는데
    왜 그런거죠?
    어떻게 해야 하는것인지 (설명)조언 부탁합니다.

  2. 우선 아래의 유튜브 영상을 보시면 Android와 iOS에서 모두 사용할 수 있는 Pull to refresh(당겨서 새로고침) 구현하실 수 있습니다.
    http://www.youtube.com/watch?v=Zp9zm1RsQ6o

    그리고, iOS와 Android 기능을 분기하는 방법은
    {$IFDEF iOS}
    {$ENDIF}

    {$IFDEF ANDROID}
    {$ENDIF}

    형태의 조건부 컴파일을 사용 해 iOS인 경우 Android인경우에 한해 코드를 구성할 수 있습니다.
    위의 조건부 컴파일을 활용해 안드로이드용 폼, iOS용 폼으로 구성할 수도 있습니다.

  3. http://blog.hjf.pe.kr/193

    위 글을 참고하시면 안드로이드에서도 가능한 당겨서 새로고침기능을 구현할 수 있습니다.

좌측에서 슬라이드로 나오는 메뉴 만들기

2014.02.06 00:20


위와 같이 왼쪽의 메뉴가 튀어나오는 UI를 구성하는 방법을 동영상으로 소개합니다.


http://joseleon.es/?p=223


소스코드 : 

drawer.zip


저작자 표시 비영리 동일 조건 변경 허락
신고
크리에이티브 커먼즈 라이선스
Creative Commons License

험프리.김현수 험프리.김현수 Firemonkey/UI / UX / 디자인

  1. Blog Icon

    AnimateDrawer.StartValue:=targetX;
    에서 에러가 나네요!

  2. Blog Icon

    AnimateDrawer: TFloatAnimation; 가 빠졌네요. 죄송

  3. http://blog.hjf.pe.kr/215
    이 글도 참고하시길 바랍니다.